Compare commits

..
104 Commits
Author SHA1 Message Date
Miroslav Stampar 85def6a662 Adding new option --results-file (thank you Hyundai) 2019-11-01 17:27:30 +01:00
Miroslav Stampar fdfcbb9161 Minor language update 2019-10-31 23:04:26 +01:00
Miroslav Stampar 096953991d Minor update 2019-10-31 22:47:36 +01:00
Miroslav Stampar a660828cec Implementation of crawling results normalization 2019-10-31 22:07:16 +01:00
Miroslav Stampar 273004396c Trivial language update 2019-10-31 21:40:28 +01:00
Miroslav Stampar 1a026e08dc Fixes special cases (e.g. cookie with asterisk skipped GET/POST tests) 2019-10-31 21:27:48 +01:00
Miroslav Stampar e21d4ae344 Minor update 2019-10-31 20:59:14 +01:00
Miroslav Stampar 3af15fee60 Trivial update 2019-10-31 20:49:38 +01:00
Miroslav Stampar 7a6e2df997 Patch for --forms/--crawl (if no protocol specified) 2019-10-31 20:40:37 +01:00
Miroslav Stampar 5c5719b81a Minor refactoring 2019-10-31 20:38:37 +01:00
Miroslav Stampar 7c9c6e4a59 Minor update (thank you Hyundai) 2019-10-31 16:57:15 +01:00
AnonProgrammer007 a422c9bc45 Update README.md (#3992) 2019-10-31 08:37:33 +01:00
Miroslav Stampar 4ede1b80a4 Minor update 2019-10-31 08:31:20 +01:00
Miroslav Stampar 869adc6fef Minor update 2019-10-29 15:07:29 +01:00
Miroslav Stampar a75ab8b128 Minor improvement of table dump formatting 2019-10-29 15:00:53 +01:00
Miroslav Stampar eec9cca85b Minor update 2019-10-29 13:52:11 +01:00
Miroslav Stampar 18d22faacd Fixes #3988 2019-10-28 12:31:17 +01:00
Miroslav Stampar a2ee93344e Adding Tarsier eyes (future logo) 2019-10-23 15:35:44 +02:00
Miroslav Stampar ab2cae3957 Implements #3971 2019-10-23 14:41:14 +02:00
Miroslav Stampar fd8028bb18 Fixes #3977 2019-10-22 14:43:29 +02:00
Miroslav Stampar 1d1da45782 Fixes #3978 2019-10-22 14:39:53 +02:00
Miroslav Stampar 8492509919 Minor update related to the #3976 2019-10-21 10:11:38 +02:00
Miroslav Stampar 9a624605b6 Falling back to web backdoor if UDF fails 2019-10-17 15:16:21 +02:00
Miroslav Stampar 1c3982c32d Update (inlining with other payload styles) 2019-10-17 13:56:41 +02:00
Miroslav Stampar 42480ba901 Update regarding #3928 2019-10-17 13:39:47 +02:00
Miroslav Stampar d02ee47157 Minor cosmetics 2019-10-17 11:53:53 +02:00
Miroslav Stampar 8340f8bcad Minor update 2019-10-17 10:53:12 +02:00
Miroslav Stampar 6bde50dbdc Patch for #3964 2019-10-15 16:08:58 +02:00
Miroslav Stampar 35893c49b8 Minor update of vuln-test 2019-10-15 12:29:39 +02:00
Miroslav Stampar 1a5ed610ce Bug fix (backslash escaping of JSON string values) 2019-10-15 01:05:34 +02:00
Miroslav Stampar 19925b806b Update regarding #3968 2019-10-14 10:42:10 +02:00
Miroslav Stampar 4b5927b734 Fixes #3967 2019-10-14 09:54:00 +02:00
Miroslav Stampar 1fa81fedf3 Patching like a boss (Issue #3962) 2019-10-10 16:23:26 +03:00
Miroslav Stampar 8407344991 Fixes #3961 2019-10-10 16:02:43 +03:00
Miroslav Stampar 17658619dc Fixes #3960 2019-10-10 15:40:56 +03:00
Miroslav Stampar 627d99089f Proper patch for #3955 2019-10-09 20:41:33 +03:00
Miroslav Stampar 9b42319d0e Proper message (Issue #3958) 2019-10-09 20:26:12 +03:00
Miroslav Stampar 63c2d2050d Fixes #3953 2019-10-09 20:06:47 +03:00
Miroslav Stampar aed137ad80 Fixes #3948 2019-10-07 14:20:18 +02:00
Miroslav Stampar 9fd4a4f0d1 Removing deprecated files (online versions available) 2019-10-04 14:15:05 +02:00
Miroslav Stampar 57511ac9df Trivial update 2019-10-04 14:12:15 +02:00
Miroslav Stampar 5f41d94602 Minor update 2019-10-04 13:51:12 +02:00
Miroslav Stampar c8a4e6378f Minor improvement for --forms 2019-10-03 15:09:59 +02:00
Miroslav Stampar 08d3228b5f Minor improvement of --forms (jquery) 2019-10-03 14:38:46 +02:00
Miroslav Stampar 5cc36b452e Minor improvement for crawling 2019-10-02 13:08:13 +02:00
Miroslav Stampar d72660ef04 Adding a support for option-less URL arg 2019-10-01 15:37:09 +02:00
Miroslav Stampar 4833fb3aa6 Adding autocompletion script 2019-09-30 17:50:25 +02:00
Miroslav Stampar f437a54561 Update regarding #3944 2019-09-27 21:59:20 +02:00
Miroslav Stampar f05f84b6e5 Minor bug fix 2019-09-27 21:35:21 +02:00
Miroslav Stampar 871ebfdb70 Fixes #3943 2019-09-27 21:03:21 +02:00
Miroslav Stampar d34619232f Implements #3940 2019-09-26 10:36:47 +02:00
Miroslav Stampar 5168daf6ce Fixes #3939 2019-09-24 10:24:43 +02:00
Miroslav Stampar 95e476d5c9 Fixes #3937 2019-09-22 20:14:43 +02:00
Miroslav Stampar cc8209d648 Patch related to the #3927 2019-09-16 19:29:38 +02:00
Miroslav Stampar 6ec6e86937 Update regarding #3928 2019-09-16 10:18:51 +02:00
Miroslav Stampar 018908c2b1 Fixes #3925 2019-09-16 10:11:28 +02:00
Miroslav Stampar 9e69d6076d Fixes #3929 2019-09-16 10:08:10 +02:00
Miroslav Stampar ad785ea0a2 Fixes #3926 2019-09-16 10:03:19 +02:00
Miroslav Stampar b51f02c2ca Patch related to #3918 2019-09-13 16:30:26 +02:00
Miroslav Stampar ea3de16f24 Implementing PEP 479 (fixes #3924) 2019-09-13 11:38:26 +02:00
Miroslav Stampar f29c4e1e07 Minor generalization regarding last commit (#3917) 2019-09-12 15:29:15 +02:00
Miroslav Stampar e8871b8a99 Fixes #3917 2019-09-12 15:20:00 +02:00
Miroslav Stampar 8b88bb82d3 Minor refactoring 2019-09-11 14:05:25 +02:00
Miroslav Stampar 30fba849e2 Implements #3916 2019-09-09 13:56:37 +02:00
Miroslav Stampar 617c336813 Minor improvements 2019-09-09 11:45:23 +02:00
Miroslav Stampar 89a5892dd9 Dirty patch for #3915 2019-09-09 11:15:13 +02:00
Miroslav Stampar af8a2afde1 Cosmetics (trivial) 2019-09-05 11:33:56 +02:00
Miroslav Stampar ceaf387533 Fixes #3912 2019-09-05 11:22:10 +02:00
Miroslav Stampar e5a1377c36 Minor update 2019-09-05 11:15:43 +02:00
Miroslav Stampar bb61b08c83 Minor update of fingerprints 2019-09-03 14:01:37 +02:00
Miroslav Stampar 2d63441cc4 Minor update 2019-09-02 15:58:57 +02:00
Miroslav Stampar 80f6460f72 Minor update for Firebird 2019-09-02 12:22:32 +02:00
Miroslav Stampar f2c2864ab4 Couple of trivial updates 2019-08-30 14:43:56 +02:00
Miroslav Stampar 9eda11d081 Trivial update 2019-08-29 17:07:16 +02:00
Miroslav Stampar 54e93e53df Fixes #3898 2019-08-27 13:41:30 +02:00
Miroslav Stampar f6e1f11711 Fixes #3897 2019-08-27 13:39:18 +02:00
Miroslav Stampar 0a21635e7f Adding a minor thing (history reasons) 2019-08-26 17:33:03 +02:00
Miroslav Stampar 83aa1ac6a7 Implements #3895 2019-08-26 17:27:32 +02:00
Miroslav Stampar 0aa15a72b0 Minor refactoring 2019-08-26 16:51:17 +02:00
Miroslav Stampar fecd830622 Minor update 2019-08-26 16:46:21 +02:00
Miroslav Stampar 32a4f6c32f Initial patch for #3894 (not final) 2019-08-25 13:20:06 +02:00
Miroslav Stampar 9a47b4025b Trivial update/patch 2019-08-23 09:49:16 +02:00
Miroslav Stampar 1979e7d75b Fixes #3888 2019-08-23 09:40:49 +02:00
Miroslav Stampar 95e6b6c0af Implements #3835 2019-08-22 11:41:06 +02:00
Miroslav Stampar 50b8de00bb Patches #3887 2019-08-22 10:43:38 +02:00
Miroslav Stampar 81289fa7cd Fixes #3886 2019-08-21 15:29:51 +02:00
Miroslav Stampar 576d81aa49 Minor log message update 2019-08-21 15:19:42 +02:00
Miroslav Stampar 009a3c8391 Minor wording update 2019-08-21 14:08:13 +02:00
Miroslav Stampar 65b0dbd4c4 Patches #3884 2019-08-20 00:35:13 +02:00
Miroslav Stampar 5f411f73e7 Fixes #3883 2019-08-20 00:22:55 +02:00
TaeGeun Moon dadb33bfdb Add Korean translation for README (#3881)
* Add Korean translation for README

* Only on main page

* Only on main page
2019-08-16 10:28:23 +02:00
Miroslav Stampar 8584c0b021 Fixes #3880 2019-08-16 09:56:03 +02:00
Miroslav Stampar 3f1a8e81b4 Adding support for #3870 2019-08-13 15:22:02 +02:00
Miroslav Stampar 0e14647573 Minor refactoring 2019-08-13 14:59:10 +02:00
Miroslav Stampar bfe8785ed5 Minor regrouping 2019-08-13 14:55:26 +02:00
Miroslav Stampar 412301bb18 Another trivial patch related to the last commit 2019-08-13 12:29:18 +02:00
Miroslav Stampar fd4becf389 Minor deprecation patch (drei) 2019-08-13 12:27:28 +02:00
Miroslav Stampar aa2682ec16 Fixes #3873 2019-08-13 12:23:45 +02:00
Miroslav Stampar 94ef433a37 Minor update for #3874 2019-08-13 11:58:33 +02:00
Miroslav Stampar 15f6796b04 Fixes #3871 2019-08-11 01:32:20 +02:00
Miroslav Stampar de63238897 Minor patch for --threads and multi Ctrl-C 2019-08-06 02:54:18 +02:00
Miroslav Stampar 1824e5b094 Trivial removal of leftover 2019-08-04 01:05:28 +02:00
Miroslav Stampar a42a7c88bd Fixes #3841 2019-08-04 01:05:13 +02:00
Miroslav Stampar b5063fc25a Implementation for #3859 2019-08-02 20:29:52 +02:00
82 changed files with 961 additions and 595 deletions
+5 -4
View File
@@ -1,8 +1,8 @@
# sqlmap
# 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/[email protected])](https://twitter.com/sqlmap)
sqlmap is an open source penetration testing tool that automates the process of detecting and exploiting SQL injection flaws and taking over of database servers. It comes with a powerful detection engine, many niche features for the ultimate penetration tester and a broad range of switches lasting from database fingerprinting, over data fetching from the database, to accessing the underlying file system and executing commands on the operating system via out-of-band connections.
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 sponsored by [Netsparker Web Application Security Scanner](https://www.netsparker.com/scan-website-security-issues/?utm_source=sqlmap.org&utm_medium=banner&utm_campaign=github).**
@@ -11,7 +11,7 @@ Screenshots
![Screenshot](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png)
You can visit the [collection of screenshots](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) demonstrating some of features on the wiki.
You can visit the [collection of screenshots](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) demonstrating some of the features on the wiki.
Installation
----
@@ -36,7 +36,7 @@ To get a list of all options and switches use:
python sqlmap.py -hh
You can find a sample run [here](https://asciinema.org/a/46601).
To get an overview of sqlmap capabilities, list of supported features and description of all options and switches, along with examples, you are advised to consult the [user's manual](https://github.com/sqlmapproject/sqlmap/wiki/Usage).
To get an overview of sqlmap capabilities, a list of supported features, and a description of all options and switches, along with examples, you are advised to consult the [user's manual](https://github.com/sqlmapproject/sqlmap/wiki/Usage).
Links
----
@@ -63,6 +63,7 @@ Translations
* [Indonesian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-id-ID.md)
* [Italian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-it-IT.md)
* [Japanese](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-ja-JP.md)
* [Korean](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-ko-KR.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)
+20
View File
@@ -192,6 +192,26 @@
/var/log/mysqld.log
/var/www/index.php
# Reference: https://github.com/sqlmapproject/sqlmap/blob/master/lib/core/settings.py#L809-L810
/var/www/index.php
/usr/local/apache/index.php
/usr/local/apache2/index.php
/usr/local/www/apache22/index.php
/usr/local/www/apache24/index.php
/usr/local/httpd/index.php
/var/www/nginx-default/index.php
/srv/www/index.php
# Reference: https://github.com/sqlmapproject/sqlmap/issues/3928
/srv/www/htdocs/index.php
/usr/local/apache2/htdocs/index.php
/usr/local/www/data/index.php
/var/apache2/htdocs/index.php
/var/www/htdocs/index.php
/var/www/html/index.php
# Reference: https://www.gracefulsecurity.com/path-traversal-cheat-sheet-linux
/etc/passwd
+17 -4
View File
@@ -1,5 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
References:
* https://en.wikipedia.org/wiki/Debian_version_history
-->
<root>
<regexp value="^([\d\.\-]+)[\-\_\ ].*">
<info dbms_version="1"/>
@@ -36,19 +41,27 @@
</regexp>
<regexp value="^([\d\.]+)[\-\_]Debian[\-\_][\d\.]+wheezy">
<info dbms_version="1" type="Linux" distrib="Debian" release="7.0" codename="wheezy"/>
<info dbms_version="1" type="Linux" distrib="Debian" release="7" codename="wheezy"/>
</regexp>
<regexp value="^([\d\.]+)[\-\_]Debian[\-\_][\d\.]+jessie">
<info dbms_version="1" type="Linux" distrib="Debian" release="8.0" codename="jessie"/>
<info dbms_version="1" type="Linux" distrib="Debian" release="8" codename="jessie"/>
</regexp>
<regexp value="^([\d\.]+)[\-\_]Debian[\-\_][\d\.]+stretch">
<info dbms_version="1" type="Linux" distrib="Debian" release="9.0" codename="stretch"/>
<info dbms_version="1" type="Linux" distrib="Debian" release="9" codename="stretch"/>
</regexp>
<regexp value="^([\d\.]+)[\-\_]Debian[\-\_][\d\.]+buster">
<info dbms_version="1" type="Linux" distrib="Debian" release="10.0" codename="buster"/>
<info dbms_version="1" type="Linux" distrib="Debian" release="10" codename="buster"/>
</regexp>
<regexp value="^([\d\.]+)[\-\_]Debian[\-\_][\d\.]+bullseye">
<info dbms_version="1" type="Linux" distrib="Debian" release="11" codename="bullseye"/>
</regexp>
<regexp value="^([\d\.]+)[\-\_]Debian[\-\_][\d\.]+bookworm">
<info dbms_version="1" type="Linux" distrib="Debian" release="12" codename="bookworm"/>
</regexp>
<regexp value="^([\d\.]+)[\-\_]Debian[\-\_][\d\.]+(sid|unstable)">
+2 -1
View File
@@ -7,13 +7,14 @@
<error regexp="Warning.*?\Wmysqli?_"/>
<error regexp="MySQLSyntaxErrorException"/>
<error regexp="valid MySQL result"/>
<error regexp="check the manual that corresponds to your (MySQL|MariaDB) server version"/>
<error regexp="check the manual that (corresponds to|fits) your (MySQL|MariaDB) server version"/>
<error regexp="Unknown column '[^ ]+' in 'field list'"/>
<error regexp="MySqlClient\."/>
<error regexp="com\.mysql\.jdbc"/>
<error regexp="Zend_Db_(Adapter|Statement)_Mysqli_Exception"/>
<error regexp="Pdo[./_\\]Mysql"/>
<error regexp="MySqlException"/>
<error regexp="SQLSTATE\[\d+\]: Syntax error or access violation"/>
</dbms>
<!-- PostgreSQL -->
+8 -8
View File
@@ -3,7 +3,7 @@
<root>
<!-- Stacked queries tests -->
<test>
<title>MySQL &gt; 5.0.11 stacked queries (comment)</title>
<title>MySQL &gt;= 5.0.12 stacked queries (comment)</title>
<stype>4</stype>
<level>2</level>
<risk>1</risk>
@@ -19,12 +19,12 @@
</response>
<details>
<dbms>MySQL</dbms>
<dbms_version>&gt; 5.0.11</dbms_version>
<dbms_version>&gt;= 5.0.12</dbms_version>
</details>
</test>
<test>
<title>MySQL &gt; 5.0.11 stacked queries</title>
<title>MySQL &gt;= 5.0.12 stacked queries</title>
<stype>4</stype>
<level>3</level>
<risk>1</risk>
@@ -39,12 +39,12 @@
</response>
<details>
<dbms>MySQL</dbms>
<dbms_version>&gt; 5.0.11</dbms_version>
<dbms_version>&gt;= 5.0.12</dbms_version>
</details>
</test>
<test>
<title>MySQL &gt; 5.0.11 stacked queries (query SLEEP - comment)</title>
<title>MySQL &gt;= 5.0.12 stacked queries (query SLEEP - comment)</title>
<stype>4</stype>
<level>3</level>
<risk>1</risk>
@@ -60,12 +60,12 @@
</response>
<details>
<dbms>MySQL</dbms>
<dbms_version>&gt; 5.0.11</dbms_version>
<dbms_version>&gt;= 5.0.12</dbms_version>
</details>
</test>
<test>
<title>MySQL &gt; 5.0.11 stacked queries (query SLEEP)</title>
<title>MySQL &gt;= 5.0.12 stacked queries (query SLEEP)</title>
<stype>4</stype>
<level>4</level>
<risk>1</risk>
@@ -80,7 +80,7 @@
</response>
<details>
<dbms>MySQL</dbms>
<dbms_version>&gt; 5.0.11</dbms_version>
<dbms_version>&gt;= 5.0.12</dbms_version>
</details>
</test>
+12 -12
View File
@@ -169,7 +169,7 @@
</test>
<test>
<title>MySQL &lt;= 5.0.11 AND time-based blind (heavy query)</title>
<title>MySQL &lt; 5.0.12 AND time-based blind (heavy query)</title>
<stype>5</stype>
<level>2</level>
<risk>2</risk>
@@ -184,12 +184,12 @@
</response>
<details>
<dbms>MySQL</dbms>
<dbms_version>&lt;= 5.0.11</dbms_version>
<dbms_version>&lt; 5.0.12</dbms_version>
</details>
</test>
<test>
<title>MySQL &lt;= 5.0.11 OR time-based blind (heavy query)</title>
<title>MySQL &lt; 5.0.12 OR time-based blind (heavy query)</title>
<stype>5</stype>
<level>2</level>
<risk>3</risk>
@@ -204,12 +204,12 @@
</response>
<details>
<dbms>MySQL</dbms>
<dbms_version>&lt;= 5.0.11</dbms_version>
<dbms_version>&lt; 5.0.12</dbms_version>
</details>
</test>
<test>
<title>MySQL &lt;= 5.0.11 AND time-based blind (heavy query - comment)</title>
<title>MySQL &lt; 5.0.12 AND time-based blind (heavy query - comment)</title>
<stype>5</stype>
<level>5</level>
<risk>2</risk>
@@ -225,12 +225,12 @@
</response>
<details>
<dbms>MySQL</dbms>
<dbms_version>&lt;= 5.0.11</dbms_version>
<dbms_version>&lt; 5.0.12</dbms_version>
</details>
</test>
<test>
<title>MySQL &lt;= 5.0.11 OR time-based blind (heavy query - comment)</title>
<title>MySQL &lt; 5.0.12 OR time-based blind (heavy query - comment)</title>
<stype>5</stype>
<level>5</level>
<risk>3</risk>
@@ -246,7 +246,7 @@
</response>
<details>
<dbms>MySQL</dbms>
<dbms_version>&lt;= 5.0.11</dbms_version>
<dbms_version>&lt; 5.0.12</dbms_version>
</details>
</test>
@@ -1506,7 +1506,7 @@
</test>
<test>
<title>MySQL &lt;= 5.0.11 time-based blind - Parameter replace (heavy queries)</title>
<title>MySQL &lt; 5.0.12 time-based blind - Parameter replace (heavy queries)</title>
<stype>5</stype>
<level>4</level>
<risk>2</risk>
@@ -1521,7 +1521,7 @@
</response>
<details>
<dbms>MySQL</dbms>
<dbms_version>&lt;= 5.0.11</dbms_version>
<dbms_version>&lt; 5.0.12</dbms_version>
</details>
</test>
@@ -1861,7 +1861,7 @@
</test>
<test>
<title>MySQL &lt;= 5.0.11 time-based blind - ORDER BY, GROUP BY clause (heavy query)</title>
<title>MySQL &lt; 5.0.12 time-based blind - ORDER BY, GROUP BY clause (heavy query)</title>
<stype>5</stype>
<level>4</level>
<risk>2</risk>
@@ -1876,7 +1876,7 @@
</response>
<details>
<dbms>MySQL</dbms>
<dbms_version>&lt;= 5.0.11</dbms_version>
<dbms_version>&lt; 5.0.12</dbms_version>
</details>
</test>
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+50
View File
@@ -0,0 +1,50 @@
# sqlmap
[![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/[email protected])](https://twitter.com/sqlmap)
sqlmap은 SQL 인젝션 결함 탐지 및 활용, 데이터베이스 서버 장악 프로세스를 자동화 하는 오픈소스 침투 테스팅 도구입니다. 최고의 침투 테스터, 데이터베이스 핑거프린팅 부터 데이터베이스 데이터 읽기, 대역 외 연결을 통한 기반 파일 시스템 접근 및 명령어 실행에 걸치는 광범위한 스위치들을 위한 강력한 탐지 엔진과 다수의 편리한 기능이 탑재되어 있습니다.
스크린샷
----
![Screenshot](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png)
또는, wiki에 나와있는 몇몇 기능을 보여주는 [스크린샷 모음](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) 을 방문하실 수 있습니다.
설치
----
[여기](https://github.com/sqlmapproject/sqlmap/tarball/master)를 클릭하여 최신 버전의 tarball 파일, 또는 [여기](https://github.com/sqlmapproject/sqlmap/zipball/master)를 클릭하여 최신 zipball 파일을 다운받으실 수 있습니다.
가장 선호되는 방법으로, [Git](https://github.com/sqlmapproject/sqlmap) 저장소를 복제하여 sqlmap을 다운로드 할 수 있습니다:
git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev
sqlmap은 [Python](http://www.python.org/download/) 버전 **2.6**, **2.7** 그리고 **3.x** 을 통해 모든 플랫폼 위에서 사용 가능합니다.
사용법
----
기본 옵션과 스위치 목록을 보려면 다음 명령어를 사용하세요:
python sqlmap.py -h
전체 옵션과 스위치 목록을 보려면 다음 명령어를 사용하세요:
python sqlmap.py -hh
[여기](https://asciinema.org/a/46601)를 통해 사용 샘플들을 확인할 수 있습니다.
sqlmap의 능력, 지원되는 기능과 모든 옵션과 스위치들의 목록을 예제와 함께 보려면, [사용자 매뉴얼](https://github.com/sqlmapproject/sqlmap/wiki/Usage)을 참고하시길 권장드립니다.
링크
----
* 홈페이지: http://sqlmap.org
* 다운로드: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) or [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master)
* RSS 피드 커밋: https://github.com/sqlmapproject/sqlmap/commits/master.atom
* Issue tracker: https://github.com/sqlmapproject/sqlmap/issues
* 사용자 매뉴얼: https://github.com/sqlmapproject/sqlmap/wiki
* 자주 묻는 질문 (FAQ): 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
+13 -4
View File
@@ -15,11 +15,13 @@ BEEP_WAV_FILENAME = os.path.join(os.path.dirname(__file__), "beep.wav")
def beep():
try:
if sys.platform == "nt":
if sys.platform.startswith("win"):
_win_wav_play(BEEP_WAV_FILENAME)
elif sys.platform == "darwin":
elif sys.platform.startswith("darwin"):
_mac_beep()
elif sys.platform.startswith("linux"):
elif sys.platform.startswith("cygwin"):
_cygwin_beep(BEEP_WAV_FILENAME)
elif any(sys.platform.startswith(_) for _ in ("linux", "freebsd")):
_linux_wav_play(BEEP_WAV_FILENAME)
else:
_speaker_beep()
@@ -34,6 +36,10 @@ def _speaker_beep():
except IOError:
pass
# Reference: https://lists.gnu.org/archive/html/emacs-devel/2014-09/msg00815.html
def _cygwin_beep(filename):
os.system("play-sound-file '%s' 2>/dev/null" % filename)
def _mac_beep():
import Carbon.Snd
Carbon.Snd.SysBeep(1)
@@ -57,7 +63,10 @@ def _linux_wav_play(filename):
class struct_pa_sample_spec(ctypes.Structure):
_fields_ = [("format", ctypes.c_int), ("rate", ctypes.c_uint32), ("channels", ctypes.c_uint8)]
pa = ctypes.cdll.LoadLibrary("libpulse-simple.so.0")
try:
pa = ctypes.cdll.LoadLibrary("libpulse-simple.so.0")
except OSError:
return
wave_file = wave.open(filename, "rb")
+45 -42
View File
@@ -76,60 +76,63 @@ def main(src, dst):
decoder = ImpactDecoder.IPDecoder()
while True:
cmd = ''
try:
cmd = ''
# Wait for incoming replies
if sock in select.select([sock], [], [])[0]:
buff = sock.recv(4096)
# Wait for incoming replies
if sock in select.select([sock], [], [])[0]:
buff = sock.recv(4096)
if 0 == len(buff):
# Socket remotely closed
sock.close()
sys.exit(0)
if 0 == len(buff):
# Socket remotely closed
sock.close()
sys.exit(0)
# Packet received; decode and display it
ippacket = decoder.decode(buff)
icmppacket = ippacket.child()
# Packet received; decode and display it
ippacket = decoder.decode(buff)
icmppacket = ippacket.child()
# If the packet matches, report it to the user
if ippacket.get_ip_dst() == src and ippacket.get_ip_src() == dst and 8 == icmppacket.get_icmp_type():
# Get identifier and sequence number
ident = icmppacket.get_icmp_id()
seq_id = icmppacket.get_icmp_seq()
data = icmppacket.get_data_as_string()
# If the packet matches, report it to the user
if ippacket.get_ip_dst() == src and ippacket.get_ip_src() == dst and 8 == icmppacket.get_icmp_type():
# Get identifier and sequence number
ident = icmppacket.get_icmp_id()
seq_id = icmppacket.get_icmp_seq()
data = icmppacket.get_data_as_string()
if len(data) > 0:
sys.stdout.write(data)
if len(data) > 0:
sys.stdout.write(data)
# Parse command from standard input
try:
cmd = sys.stdin.readline()
except:
pass
# Parse command from standard input
try:
cmd = sys.stdin.readline()
except:
pass
if cmd == 'exit\n':
return
if cmd == 'exit\n':
return
# Set sequence number and identifier
icmp.set_icmp_id(ident)
icmp.set_icmp_seq(seq_id)
# Set sequence number and identifier
icmp.set_icmp_id(ident)
icmp.set_icmp_seq(seq_id)
# Include the command as data inside the ICMP packet
icmp.contains(ImpactPacket.Data(cmd))
# Include the command as data inside the ICMP packet
icmp.contains(ImpactPacket.Data(cmd))
# Calculate its checksum
icmp.set_icmp_cksum(0)
icmp.auto_checksum = 1
# Calculate its checksum
icmp.set_icmp_cksum(0)
icmp.auto_checksum = 1
# Have the IP packet contain the ICMP packet (along with its payload)
ip.contains(icmp)
# Have the IP packet contain the ICMP packet (along with its payload)
ip.contains(icmp)
try:
# Send it to the target host
sock.sendto(ip.get_packet(), (dst, 0))
except socket.error as ex:
sys.stderr.write("'%s'\n" % ex)
sys.stderr.flush()
try:
# Send it to the target host
sock.sendto(ip.get_packet(), (dst, 0))
except socket.error as ex:
sys.stderr.write("'%s'\n" % ex)
sys.stderr.flush()
except:
break
if __name__ == '__main__':
if len(sys.argv) < 3:
-17
View File
@@ -1,17 +0,0 @@
To use safe2bin.py you need to pass it the original file,
and optionally the output file name.
Example:
$ python ./safe2bin.py -i output.txt -o output.txt.bin
This will create an binary decoded file output.txt.bin. For example,
if the content of output.txt is: "\ttest\t\x32\x33\x34\nnewline" it will
be decoded to: " test 234
newline"
If you skip the output file name, general rule is that the binary
file names are suffixed with the string '.bin'. So, that means that
the upper example can also be written in the following form:
$ python ./safe2bin.py -i output.txt
-8
View File
@@ -1,8 +0,0 @@
#!/usr/bin/env python
"""
Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""
pass
+9
View File
@@ -0,0 +1,9 @@
#/usr/bin/env bash
# source ./extra/shutils/autocompletion.sh
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
WORDLIST=`python "$DIR/../../sqlmap.py" -hh | grep -Eo '\s\--?\w[^ =,]*' | grep -vF '..' | paste -sd "" -`
complete -W "$WORDLIST" sqlmap
complete -W "$WORDLIST" ./sqlmap.py
+1 -1
View File
@@ -7,7 +7,7 @@
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; done
for i in $(find . -iname "*.py" | grep -v __init__); do PYTHONWARNINGS=all python3.7 -m compileall $i | sed 's/Compiling/Checking/g'; done
unset SQLMAP_DREI
source `dirname "$0"`"/junk.sh"
+6 -2
View File
@@ -9,6 +9,7 @@ See the file 'LICENSE' for copying permission
from __future__ import print_function
import json
import re
import sqlite3
import sys
@@ -99,10 +100,13 @@ class ReqHandler(BaseHTTPRequestHandler):
return
if hasattr(self, "data"):
params.update(parse_qs(self.data))
if self.data.startswith('{') and self.data.endswith('}'):
params.update(json.loads(self.data))
else:
params.update(parse_qs(self.data))
for key in params:
if params[key]:
if params[key] and isinstance(params[key], (tuple, list)):
params[key] = params[key][-1]
self.url, self.params = path, params
+11
View File
@@ -73,6 +73,7 @@ from lib.core.settings import BOUNDED_INJECTION_MARKER
from lib.core.settings import CANDIDATE_SENTENCE_MIN_LENGTH
from lib.core.settings import CHECK_INTERNET_ADDRESS
from lib.core.settings import CHECK_INTERNET_VALUE
from lib.core.settings import DEFAULT_COOKIE_DELIMITER
from lib.core.settings import DEFAULT_GET_POST_DELIMITER
from lib.core.settings import DUMMY_NON_SQLI_CHECK_APPENDIX
from lib.core.settings import FI_ERROR_REGEX
@@ -1559,6 +1560,16 @@ 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:
candidate = DEFAULT_COOKIE_DELIMITER.join("%s=%s" % (_.name, _.value) for _ in conf.cj)
message = "you have not declared cookie(s), while "
message += "server wants to set its own ('%s'). " % re.sub(r"(=[^=;]{10}[^=;])[^=;]+([^=;]{10})", r"\g<1>...\g<2>", candidate)
message += "Do you want to use those [Y/n] "
if readInput(message, default='Y', boolean=True):
kb.mergeCookies = True
conf.httpHeaders.append((HTTP_HEADER.COOKIE, candidate))
return True
def checkInternet():
+16 -9
View File
@@ -31,6 +31,7 @@ from lib.core.common import getSafeExString
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 isListLike
from lib.core.common import parseTargetUrl
from lib.core.common import popValue
@@ -129,7 +130,7 @@ def _selectInjection():
message += "[q] Quit"
choice = readInput(message, default='0').upper()
if choice.isdigit() and int(choice) < len(kb.injections) and int(choice) >= 0:
if isDigit(choice) and int(choice) < len(kb.injections) and int(choice) >= 0:
index = int(choice)
elif choice == 'Q':
raise SqlmapUserQuitException
@@ -262,7 +263,7 @@ def _saveToResultsFile():
conf.resultsFP.flush()
except IOError as ex:
errMsg = "unable to write to the results file '%s' ('%s'). " % (conf.resultsFilename, getSafeExString(ex))
errMsg = "unable to write to the results file '%s' ('%s'). " % (conf.resultsFile, getSafeExString(ex))
raise SqlmapSystemException(errMsg)
@stackedmethod
@@ -374,7 +375,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) if conf.data else "")
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)
if conf.forms and conf.method:
if conf.method == HTTPMETHOD.GET and targetUrl.find("?") == -1:
@@ -389,7 +390,7 @@ def start():
break
else:
if conf.method != HTTPMETHOD.GET:
message = "Edit %s data [default: %s]%s: " % (conf.method, urlencode(conf.data) if conf.data else "None", " (Warning: blank fields detected)" if conf.data and extractRegexResult(EMPTY_FORM_FIELDS_REGEX, conf.data) else "")
message = "Edit %s data [default: %s]%s: " % (conf.method, urlencode(conf.data or "") if re.search(r"\A\s*[<{]", conf.data or "None") is None else conf.data, " (Warning: blank fields detected)" if conf.data and extractRegexResult(EMPTY_FORM_FIELDS_REGEX, conf.data) else "")
conf.data = readInput(message, default=conf.data)
conf.data = _randomFillBlankFields(conf.data)
conf.data = urldecode(conf.data) if conf.data and urlencode(DEFAULT_GET_POST_DELIMITER, None) not in conf.data else conf.data
@@ -422,6 +423,15 @@ def start():
if not checkConnection(suppressOutput=conf.forms) or not checkString() or not checkRegexp():
continue
if conf.rParam and kb.originalPage:
kb.randomPool = dict([_ for _ in kb.randomPool.items() if isinstance(_[1], list)])
for match in re.finditer(r"(?si)<select[^>]+\bname\s*=\s*[\"']([^\"']+)(.+?)</select>", kb.originalPage):
name, _ = match.groups()
options = tuple(re.findall(r"<option[^>]+\bvalue\s*=\s*[\"']([^\"']+)", _))
if options:
kb.randomPool[name] = options
checkWaf()
if conf.nullConnection:
@@ -475,9 +485,6 @@ def start():
if skip:
continue
if kb.testOnlyCustom and place not in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER):
continue
if place not in conf.paramDict:
continue
@@ -731,9 +738,9 @@ def start():
logger.info("fetched data logged to text files under '%s'" % conf.outputPath)
if conf.multipleTargets:
if conf.resultsFilename:
if conf.resultsFile:
infoMsg = "you can find results of scanning in multiple targets "
infoMsg += "mode inside the CSV file '%s'" % conf.resultsFilename
infoMsg += "mode inside the CSV file '%s'" % conf.resultsFile
logger.info(infoMsg)
return True
+19 -11
View File
@@ -5,7 +5,6 @@ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""
import base64
import re
from lib.core.common import Backend
@@ -27,6 +26,7 @@ from lib.core.common import unArrayizeValue
from lib.core.common import urlencode
from lib.core.common import zeroDepthSearch
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
@@ -50,7 +50,9 @@ from lib.core.settings import PAYLOAD_DELIMITER
from lib.core.settings import REPLACEMENT_MARKER
from lib.core.settings import SINGLE_QUOTE_MARKER
from lib.core.settings import SLEEP_TIME_MARKER
from lib.core.settings import UNICODE_ENCODING
from lib.core.unescaper import unescaper
from thirdparty import six
class Agent(object):
"""
@@ -169,17 +171,21 @@ class Agent(object):
if re.sub(r" \(.+", "", parameter) in conf.base64Parameter:
# TODO: support for POST_HINT
newValue = base64.b64encode(newValue)
origValue = base64.b64encode(origValue)
newValue = encodeBase64(newValue, binary=False, encoding=conf.encoding or UNICODE_ENCODING)
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"' % newValue
newValue = '"%s"' % self.addPayloadDelimiters(newValue)
elif kb.postHint == POST_HINT.JSON_LIKE and not isNumber(newValue) and "'%s'" % _ not in paramString:
newValue = "'%s'" % newValue
newValue = "'%s'" % self.addPayloadDelimiters(newValue)
else:
newValue = self.addPayloadDelimiters(newValue)
newValue = newValue.replace(kb.customInjectionMark, REPLACEMENT_MARKER)
retVal = paramString.replace(_, self.addPayloadDelimiters(newValue))
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))
@@ -308,7 +314,7 @@ class Agent(object):
return re.sub(r";\W*;", ";", expression) if trimEmpty else expression
def cleanupPayload(self, payload, origValue=None):
if payload is None:
if not isinstance(payload, six.string_types):
return
replacements = {
@@ -333,6 +339,7 @@ class Agent(object):
if origValue is not None:
origValue = getUnicode(origValue)
if "[ORIGVALUE]" in payload:
payload = getUnicode(payload).replace("[ORIGVALUE]", origValue if origValue.isdigit() else unescaper.escape("'%s'" % origValue))
if "[ORIGINAL]" in payload:
@@ -351,6 +358,7 @@ class Agent(object):
inferenceQuery = inference.query
payload = payload.replace(INFERENCE_MARKER, inferenceQuery)
elif not kb.testMode:
errMsg = "invalid usage of inference payload without "
errMsg += "knowledge of underlying DBMS"
@@ -393,7 +401,7 @@ class Agent(object):
if "hex" in rootQuery:
hexField = rootQuery.hex.query % field
else:
warnMsg = "switch '--hex' is currently not supported on DBMS %s" % Backend.getIdentifiedDbms()
warnMsg = "switch '--hex' is currently not supported on DBMS '%s'" % Backend.getIdentifiedDbms()
singleTimeWarnMessage(warnMsg)
return hexField
@@ -651,7 +659,7 @@ class Agent(object):
concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.chars.start, 1)
_ = unArrayizeValue(zeroDepthSearch(concatenatedQuery, " FROM "))
concatenatedQuery = "%s||'%s'%s" % (concatenatedQuery[:_], kb.chars.stop, concatenatedQuery[_:])
concatenatedQuery = re.sub(r"('%s'\|\|)(.+)(%s)" % (kb.chars.start, re.escape(castedFields)), r"\g<2>\g<1>\g<3>", concatenatedQuery)
concatenatedQuery = re.sub(r"('%s'\|\|)(.+?)(%s)" % (kb.chars.start, re.escape(castedFields)), r"\g<2>\g<1>\g<3>", concatenatedQuery)
elif fieldsSelect:
concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.chars.start, 1)
concatenatedQuery += "||'%s'" % kb.chars.stop
@@ -1007,7 +1015,7 @@ class Agent(object):
limitedQuery = "%s WHERE %s " % (limitedQuery, self.nullAndCastField(uniqueField or field))
limitedQuery += "NOT IN (%s" % (limitStr % num)
limitedQuery += "%s %s ORDER BY %s) ORDER BY %s" % (self.nullAndCastField(uniqueField or field), fromFrom, uniqueField or "1", uniqueField or "1")
limitedQuery += "%s %s ORDER BY %s) ORDER BY %s" % (self.nullAndCastField(uniqueField or field), fromFrom, uniqueField or '1', uniqueField or '1')
else:
match = re.search(r" ORDER BY (\w+)\Z", query)
field = match.group(1) if match else field
@@ -1081,7 +1089,7 @@ class Agent(object):
Removes payload delimiters from inside the input string
"""
return value.replace(PAYLOAD_DELIMITER, '') if value else value
return value.replace(PAYLOAD_DELIMITER, "") if value else value
def extractPayload(self, value):
"""
+121 -76
View File
@@ -46,7 +46,6 @@ from xml.sax import SAXParseException
from extra.beep.beep import beep
from extra.cloak.cloak import decloak
from extra.safe2bin.safe2bin import safecharencode
from lib.core.bigarray import BigArray
from lib.core.compat import cmp
from lib.core.compat import round
@@ -180,6 +179,7 @@ from lib.core.settings import VERSION_STRING
from lib.core.settings import ZIP_HEADER
from lib.core.settings import WEBSCARAB_SPLITTER
from lib.core.threads import getCurrentThreadData
from lib.utils.safe2bin import safecharencode
from lib.utils.sqlalchemy import _sqlalchemy
from thirdparty import six
from thirdparty.clientform.clientform import ParseResponse
@@ -623,8 +623,8 @@ def paramToDict(place, parameters=None):
if parameter in (conf.base64Parameter or []):
try:
oldValue = value
value = decodeBase64(value, binary=False)
parameters = re.sub(r"\b%s\b" % re.escape(oldValue), value, parameters)
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:
errMsg = "parameter '%s' does not contain " % parameter
errMsg += "valid Base64 encoded value ('%s')" % value
@@ -701,7 +701,7 @@ def paramToDict(place, parameters=None):
message += "has boundaries. Do you want to inject inside? ('%s') [y/N] " % getUnicode(_)
if readInput(message, default='N', boolean=True):
testableParameters[parameter] = re.sub(r"\b(%s\s*=\s*)%s" % (re.escape(parameter), re.escape(testableParameters[parameter])), (r"\g<1>%s" % re.sub(regex, r"\g<1>%s\g<2>" % BOUNDED_INJECTION_MARKER, testableParameters[parameter])).replace("\\", r"\\"), parameters)
testableParameters[parameter] = re.sub(r"\b(%s\s*=\s*)%s" % (re.escape(parameter), re.escape(testableParameters[parameter])), (r"\g<1>%s" % re.sub(regex, r"\g<1>%s\g<2>" % BOUNDED_INJECTION_MARKER, testableParameters[parameter].replace("\\", r"\\"))), parameters)
break
if conf.testParameter:
@@ -1245,6 +1245,22 @@ def isZipFile(filename):
return openFile(filename, "rb", encoding=None).read(len(ZIP_HEADER)) == ZIP_HEADER
def isDigit(value):
"""
Checks if provided (string) value consists of digits (Note: Python's isdigit() is problematic)
>>> u'\xb2'.isdigit()
True
>>> isDigit(u'\xb2')
False
>>> isDigit('123456')
True
>>> isDigit('3b3')
False
"""
return re.search(r"\A[0-9]+\Z", value or "") is not None
def checkFile(filename, raiseOnError=True):
"""
Checks for file existence and readability
@@ -1285,36 +1301,43 @@ def banner():
"""
if not any(_ in sys.argv for _ in ("--version", "--api")) and not conf.get("disableBanner"):
_ = BANNER
result = BANNER
if not IS_TTY or "--disable-coloring" in sys.argv:
_ = clearColors(_)
result = clearColors(result)
elif IS_WIN:
coloramainit()
dataToStdout(_, forceOutput=True)
dataToStdout(result, forceOutput=True)
def parsePasswordHash(password):
"""
In case of Microsoft SQL Server password hash value is expanded to its components
>>> pushValue(kb.forcedDbms)
>>> kb.forcedDbms = DBMS.MSSQL
>>> "salt: 4086ceb6" in parsePasswordHash("0x01004086ceb60c90646a8ab9889fe3ed8e5c150b5460ece8425a")
True
>>> kb.forcedDbms = popValue()
"""
blank = " " * 8
if not password or password == " ":
password = NULL
if isNoneValue(password) or password == " ":
retVal = NULL
else:
retVal = password
if Backend.isDbms(DBMS.MSSQL) and password != NULL and isHexEncodedString(password):
hexPassword = password
password = "%s\n" % hexPassword
password += "%sheader: %s\n" % (blank, hexPassword[:6])
password += "%ssalt: %s\n" % (blank, hexPassword[6:14])
password += "%smixedcase: %s\n" % (blank, hexPassword[14:54])
if Backend.isDbms(DBMS.MSSQL) and retVal != NULL and isHexEncodedString(password):
retVal = "%s\n" % password
retVal += "%sheader: %s\n" % (blank, password[:6])
retVal += "%ssalt: %s\n" % (blank, password[6:14])
retVal += "%smixedcase: %s\n" % (blank, password[14:54])
if not Backend.isVersionWithin(("2005", "2008")):
password += "%suppercase: %s" % (blank, hexPassword[54:])
if password[54:]:
retVal += "%suppercase: %s" % (blank, password[54:])
return password
return retVal
def cleanQuery(query):
"""
@@ -3171,7 +3194,10 @@ def isDBMSVersionAtLeast(minimum):
parts[1] = filterStringValue(parts[1], '[0-9]')
version = '.'.join(parts)
version = float(filterStringValue(version, '[0-9.]')) + correction
try:
version = float(filterStringValue(version, '[0-9.]')) + correction
except ValueError:
return None
if isinstance(minimum, six.string_types):
if '.' in minimum:
@@ -3575,7 +3601,7 @@ def decodeIntToUnicode(value):
# Note: https://github.com/sqlmapproject/sqlmap/issues/1531
retVal = getUnicode(raw, conf.encoding or UNICODE_ENCODING)
elif Backend.isDbms(DBMS.MSSQL):
retVal = getUnicode(raw, "UTF-16-BE")
retVal = getUnicode(raw, "UTF-16-BE") # References: https://docs.microsoft.com/en-us/sql/relational-databases/collations/collation-and-unicode-support?view=sql-server-2017 and https://stackoverflow.com/a/14488478
elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.ORACLE):
retVal = _unichr(value)
else:
@@ -3662,7 +3688,7 @@ def getLatestRevision():
"""
retVal = None
req = _urllib.request.Request(url="https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/lib/core/settings.py")
req = _urllib.request.Request(url="https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/lib/core/settings.py", headers={HTTP_HEADER.USER_AGENT: fetchRandomAgent()})
try:
content = getUnicode(_urllib.request.urlopen(req).read())
@@ -3794,7 +3820,7 @@ def maskSensitiveData(msg):
retVal = retVal.replace(value, '*' * len(value))
# Just in case (for problematic parameters regarding user encoding)
for match in re.finditer(r"(?i)[ -]-(u|url|data|cookie|auth-\w+|proxy)( |=)(.*?)(?= -?-[a-z]|\Z)", retVal):
for match in re.finditer(r"(?i)[ -]-(u|url|data|cookie|auth-\w+|proxy|host|referer|headers?|H)( |=)(.*?)(?= -?-[a-z]|\Z)", retVal):
retVal = retVal.replace(match.group(3), '*' * len(match.group(3)))
# Fail-safe substitutions
@@ -4410,54 +4436,73 @@ def findPageForms(content, url, raise_=False, addToTargets=False):
except:
pass
if forms:
for form in forms:
try:
for control in form.controls:
if hasattr(control, "items") and not any((control.disabled, control.readonly)):
# if control has selectable items select first non-disabled
for item in control.items:
if not item.disabled:
if not item.selected:
item.selected = True
break
for form in forms or []:
try:
for control in form.controls:
if hasattr(control, "items") and not any((control.disabled, control.readonly)):
# if control has selectable items select first non-disabled
for item in control.items:
if not item.disabled:
if not item.selected:
item.selected = True
break
if conf.crawlExclude and re.search(conf.crawlExclude, form.action or ""):
dbgMsg = "skipping '%s'" % form.action
logger.debug(dbgMsg)
continue
if conf.crawlExclude and re.search(conf.crawlExclude, form.action or ""):
dbgMsg = "skipping '%s'" % form.action
logger.debug(dbgMsg)
continue
request = form.click()
except (ValueError, TypeError) as ex:
errMsg = "there has been a problem while "
errMsg += "processing page forms ('%s')" % getSafeExString(ex)
if raise_:
raise SqlmapGenericException(errMsg)
else:
logger.debug(errMsg)
request = form.click()
except (ValueError, TypeError) as ex:
errMsg = "there has been a problem while "
errMsg += "processing page forms ('%s')" % getSafeExString(ex)
if raise_:
raise SqlmapGenericException(errMsg)
else:
url = urldecode(request.get_full_url(), kb.pageEncoding)
method = request.get_method()
data = request.data
data = urldecode(data, kb.pageEncoding, spaceplus=False)
logger.debug(errMsg)
else:
url = urldecode(request.get_full_url(), kb.pageEncoding)
method = request.get_method()
data = request.data
data = urldecode(data, kb.pageEncoding, spaceplus=False)
if not data and method and method.upper() == HTTPMETHOD.POST:
debugMsg = "invalid POST form with blank data detected"
logger.debug(debugMsg)
if not data and method and method.upper() == HTTPMETHOD.POST:
debugMsg = "invalid POST form with blank data detected"
logger.debug(debugMsg)
continue
# flag to know if we are dealing with the same target host
_ = checkSameHost(response.geturl(), url)
if conf.scope:
if not re.search(conf.scope, url, re.I):
continue
elif not _:
continue
else:
target = (url, method, data, conf.cookie, None)
retVal.add(target)
# flag to know if we are dealing with the same target host
_ = checkSameHost(response.geturl(), url)
for match in re.finditer(r"\.post\(['\"]([^'\"]*)['\"],\s*\{([^}]*)\}", content):
url = _urllib.parse.urljoin(url, htmlUnescape(match.group(1)))
data = ""
if conf.scope:
if not re.search(conf.scope, url, re.I):
continue
elif not _:
continue
else:
target = (url, method, data, conf.cookie, None)
retVal.add(target)
else:
for name, value in re.findall(r"['\"]?(\w+)['\"]?\s*:\s*(['\"][^'\"]+)?", match.group(2)):
data += "%s=%s%s" % (name, value, DEFAULT_GET_POST_DELIMITER)
data = data.rstrip(DEFAULT_GET_POST_DELIMITER)
retVal.add((url, HTTPMETHOD.POST, data, conf.cookie, None))
for match in re.finditer(r"(?s)(\w+)\.open\(['\"]POST['\"],\s*['\"]([^'\"]+)['\"]\).*?\1\.send\(([^)]+)\)", content):
url = _urllib.parse.urljoin(url, htmlUnescape(match.group(2)))
data = match.group(3)
data = re.sub(r"\s*\+\s*[^\s'\"]+|[^\s'\"]+\s*\+\s*", "", data)
data = data.strip("['\"]")
retVal.add((url, HTTPMETHOD.POST, data, conf.cookie, None))
if not retVal:
errMsg = "there were no forms found at the given target URL"
if raise_:
raise SqlmapGenericException(errMsg)
@@ -4658,18 +4703,19 @@ def decodeDbmsHexValue(value, raw=False):
else:
retVal = decodeHex(value)
if not kb.binaryField and not raw:
if Backend.isDbms(DBMS.MSSQL) and value.startswith("0x"):
try:
retVal = retVal.decode("utf-16-le")
except UnicodeDecodeError:
pass
if not raw:
if not kb.binaryField:
if Backend.isDbms(DBMS.MSSQL) and value.startswith("0x"):
try:
retVal = retVal.decode("utf-16-le")
except UnicodeDecodeError:
pass
elif Backend.getIdentifiedDbms() in (DBMS.HSQLDB, DBMS.H2):
try:
retVal = retVal.decode("utf-16-be")
except UnicodeDecodeError:
pass
elif Backend.getIdentifiedDbms() in (DBMS.HSQLDB, DBMS.H2):
try:
retVal = retVal.decode("utf-16-be")
except UnicodeDecodeError:
pass
if not isinstance(retVal, six.text_type):
retVal = getUnicode(retVal, conf.encoding or UNICODE_ENCODING)
@@ -4881,7 +4927,6 @@ def zeroDepthSearch(expression, value):
elif expression[index] == ')':
depth -= 1
elif depth == 0:
found = False
if value.startswith('[') and value.endswith(']'):
if re.search(value, expression[index:index + 1]):
retVal.append(index)
@@ -4970,7 +5015,7 @@ def parseRequestFile(reqFile, checkParams=True):
port, request = match.groups()
try:
request = decodeBase64(request, binary=False)
except binascii.Error:
except (binascii.Error, TypeError):
continue
_ = re.search(r"%s:.+" % re.escape(HTTP_HEADER.HOST), request)
if _:
+2 -2
View File
@@ -13,6 +13,7 @@ import math
import os
import random
import sys
import time
import uuid
class WichmannHill(random.Random):
@@ -40,7 +41,6 @@ class WichmannHill(random.Random):
try:
a = int(binascii.hexlify(os.urandom(16)), 16)
except NotImplementedError:
import time
a = int(time.time() * 256) # use fractional seconds
if not isinstance(a, int):
@@ -132,7 +132,6 @@ class WichmannHill(random.Random):
raise ValueError('seeds must be in range(0, 256)')
if 0 == x == y == z:
# Initialize from current time
import time
t = int(time.time() * 256)
t = int((t & 0xffffff) ^ (t >> 24))
t, x = divmod(t, 256)
@@ -204,6 +203,7 @@ def round(x, d=0):
else:
return float(math.ceil((x * p) - 0.5)) / p
# Reference: https://code.activestate.com/recipes/576653-convert-a-cmp-function-to-a-key-function/
def cmp_to_key(mycmp):
"""Convert a cmp= function into a key= function"""
class K(object):
+25 -8
View File
@@ -184,7 +184,7 @@ def encodeHex(value, binary=True):
return retVal
def decodeBase64(value, binary=True):
def decodeBase64(value, binary=True, encoding=None):
"""
Returns a decoded representation of provided Base64 value
@@ -197,11 +197,11 @@ def decodeBase64(value, binary=True):
retVal = base64.b64decode(value)
if not binary:
retVal = getText(retVal)
retVal = getText(retVal, encoding)
return retVal
def encodeBase64(value, binary=True):
def encodeBase64(value, binary=True, encoding=None):
"""
Returns a decoded representation of provided Base64 value
@@ -212,12 +212,12 @@ def encodeBase64(value, binary=True):
"""
if isinstance(value, six.text_type):
value = value.encode(UNICODE_ENCODING)
value = value.encode(encoding or UNICODE_ENCODING)
retVal = base64.b64encode(value)
if not binary:
retVal = getText(retVal)
retVal = getText(retVal, encoding)
return retVal
@@ -263,7 +263,7 @@ def getOrds(value):
def getUnicode(value, encoding=None, noneToNull=False):
"""
Return the unicode representation of the supplied value:
Returns the unicode representation of the supplied value
>>> getUnicode('test') == u'test'
True
@@ -305,7 +305,7 @@ def getUnicode(value, encoding=None, noneToNull=False):
except UnicodeDecodeError:
return six.text_type(str(value), errors="ignore") # encoding ignored for non-basestring instances
def getText(value):
def getText(value, encoding=None):
"""
Returns textual value of a given value (Note: not necessary Unicode on Python2)
@@ -318,7 +318,7 @@ def getText(value):
retVal = value
if isinstance(value, six.binary_type):
retVal = getUnicode(value)
retVal = getUnicode(value, encoding)
if six.PY2:
try:
@@ -375,3 +375,20 @@ def stdoutEncode(value):
retVal = value
return retVal
def getConsoleLength(value):
"""
Returns console width of unicode values
>>> getConsoleLength("abc")
3
>>> getConsoleLength(u"\\u957f\\u6c5f")
4
"""
if isinstance(value, six.text_type):
retVal = sum((2 if ord(_) >= 0x3000 else 1) for _ in value)
else:
retVal = len(value)
return retVal
+1 -1
View File
@@ -85,7 +85,7 @@ def lockedmethod(f):
@functools.wraps(f)
def _(*args, **kwargs):
if f not in _method_locks:
_method_locks[f] = threading.Lock()
_method_locks[f] = threading.RLock()
with _method_locks[f]:
result = f(*args, **kwargs)
+1
View File
@@ -20,6 +20,7 @@ _defaults = {
"level": 1,
"risk": 1,
"dumpFormat": "CSV",
"tablePrefix": "sqlmap",
"technique": "BEUSTQ",
"torType": "SOCKS5",
}
+22 -21
View File
@@ -13,7 +13,6 @@ import shutil
import tempfile
import threading
from extra.safe2bin.safe2bin import safechardecode
from lib.core.common import Backend
from lib.core.common import checkFile
from lib.core.common import dataToDumpFile
@@ -29,6 +28,7 @@ from lib.core.common import safeCSValue
from lib.core.common import unsafeSQLIdentificatorNaming
from lib.core.compat import xrange
from lib.core.convert import getBytes
from lib.core.convert import getConsoleLength
from lib.core.convert import getText
from lib.core.convert import getUnicode
from lib.core.data import conf
@@ -53,6 +53,7 @@ from lib.core.settings import UNICODE_ENCODING
from lib.core.settings import UNSAFE_DUMP_FILEPATH_REPLACEMENT
from lib.core.settings import VERSION_STRING
from lib.core.settings import WINDOWS_RESERVED_NAMES
from lib.utils.safe2bin import safechardecode
from thirdparty import six
from thirdparty.magic import magic
@@ -197,7 +198,7 @@ class Dump(object):
self._areAdmins = userSettings[1]
userSettings = userSettings[0]
users = list(userSettings.keys())
users = [_ for _ in userSettings.keys() if _ is not None]
users.sort(key=lambda _: _.lower() if hasattr(_, "lower") else _)
if conf.api:
@@ -245,7 +246,7 @@ class Dump(object):
if table and isListLike(table):
table = table[0]
maxlength = max(maxlength, len(unsafeSQLIdentificatorNaming(normalizeUnicode(table) or getUnicode(table))))
maxlength = max(maxlength, getConsoleLength(unsafeSQLIdentificatorNaming(getUnicode(table))))
lines = "-" * (int(maxlength) + 2)
@@ -266,7 +267,7 @@ class Dump(object):
table = table[0]
table = unsafeSQLIdentificatorNaming(table)
blank = " " * (maxlength - len(normalizeUnicode(table) or getUnicode(table)))
blank = " " * (maxlength - getConsoleLength(getUnicode(table)))
self._write("| %s%s |" % (table, blank))
self._write("+%s+\n" % lines)
@@ -361,7 +362,7 @@ class Dump(object):
for ctables in dbTables.values():
for tables in ctables.values():
for table in tables:
maxlength1 = max(maxlength1, len(normalizeUnicode(table) or getUnicode(table)))
maxlength1 = max(maxlength1, getConsoleLength(getUnicode(table)))
for db, counts in dbTables.items():
self._write("Database: %s" % unsafeSQLIdentificatorNaming(db) if db else "Current database")
@@ -387,7 +388,7 @@ class Dump(object):
tables.sort(key=lambda _: _.lower() if hasattr(_, "lower") else _)
for table in tables:
blank1 = " " * (maxlength1 - len(normalizeUnicode(table) or getUnicode(table)))
blank1 = " " * (maxlength1 - getConsoleLength(getUnicode(table)))
blank2 = " " * (maxlength2 - len(str(count)))
self._write("| %s%s | %d%s |" % (table, blank1, count, blank2))
@@ -547,7 +548,7 @@ class Dump(object):
column = unsafeSQLIdentificatorNaming(column)
maxlength = int(info["length"])
blank = " " * (maxlength - len(column))
blank = " " * (maxlength - getConsoleLength(column))
self._write("| %s%s" % (column, blank), newline=False)
@@ -602,7 +603,7 @@ class Dump(object):
values.append(value)
maxlength = int(info["length"])
blank = " " * (maxlength - len(value))
blank = " " * (maxlength - getConsoleLength(value))
self._write("| %s%s" % (value, blank), newline=False, console=console)
if len(value) > MIN_BINARY_DISK_DUMP_SIZE and r'\x' in value:
@@ -676,28 +677,28 @@ class Dump(object):
else:
colConsiderStr = " '%s' was" % unsafeSQLIdentificatorNaming(column)
msg = "column%s found in the " % colConsiderStr
msg += "following databases:"
self._write(msg)
_ = {}
found = {}
for db, tblData in dbs.items():
for tbl, colData in tblData.items():
for col, dataType in colData.items():
if column.lower() in col.lower():
if db in _:
if tbl in _[db]:
_[db][tbl][col] = dataType
if db in found:
if tbl in found[db]:
found[db][tbl][col] = dataType
else:
_[db][tbl] = {col: dataType}
found[db][tbl] = {col: dataType}
else:
_[db] = {}
_[db][tbl] = {col: dataType}
found[db] = {}
found[db][tbl] = {col: dataType}
continue
self.dbTableColumns(_)
if found:
msg = "column%s found in the " % colConsiderStr
msg += "following databases:"
self._write(msg)
self.dbTableColumns(found)
def sqlQuery(self, query, queryRes):
self.string(query, queryRes, content_type=CONTENT_TYPE.SQL_QUERY)
+6 -6
View File
@@ -130,12 +130,12 @@ class HASH(object):
MSSQL_NEW = r'(?i)\A0x0200[0-9a-f]{8}[0-9a-f]{128}\Z'
ORACLE = r'(?i)\As:[0-9a-f]{60}\Z'
ORACLE_OLD = r'(?i)\A[0-9a-f]{16}\Z'
MD5_GENERIC = r'(?i)\A[0-9a-f]{32}\Z'
SHA1_GENERIC = r'(?i)\A[0-9a-f]{40}\Z'
MD5_GENERIC = r'(?i)\A(0x)?[0-9a-f]{32}\Z'
SHA1_GENERIC = r'(?i)\A(0x)?[0-9a-f]{40}\Z'
SHA224_GENERIC = r'(?i)\A[0-9a-f]{56}\Z'
SHA256_GENERIC = r'(?i)\A[0-9a-f]{64}\Z'
SHA256_GENERIC = r'(?i)\A(0x)?[0-9a-f]{64}\Z'
SHA384_GENERIC = r'(?i)\A[0-9a-f]{96}\Z'
SHA512_GENERIC = r'(?i)\A[0-9a-f]{128}\Z'
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'
@@ -244,8 +244,8 @@ class HASHDB_KEYS(object):
OS = "OS"
class REDIRECTION(object):
YES = "Y"
NO = "N"
YES = 'Y'
NO = 'N'
class PAYLOAD(object):
SQLINJECTION = {
+58 -18
View File
@@ -109,6 +109,7 @@ from lib.core.settings import DEFAULT_TOR_HTTP_PORTS
from lib.core.settings import DEFAULT_TOR_SOCKS_PORTS
from lib.core.settings import DEFAULT_USER_AGENT
from lib.core.settings import DUMMY_URL
from lib.core.settings import IGNORE_CODE_WILDCARD
from lib.core.settings import IS_WIN
from lib.core.settings import KB_CHARS_BOUNDARY_CHAR
from lib.core.settings import KB_CHARS_LOW_FREQUENCY_ALPHABET
@@ -314,7 +315,7 @@ def _setRequestFromFile():
if url is None:
errMsg = "specified file '%s' " % requestFile
errMsg += "does not contain a valid HTTP request"
errMsg += "does not contain a usable HTTP request (with parameters)"
raise SqlmapDataException(errMsg)
if conf.secondReq:
@@ -341,10 +342,15 @@ def _setCrawler():
if conf.bulkFile:
targets = getFileItems(conf.bulkFile)
else:
targets = parseSitemap(conf.sitemapUrl)
targets = list(parseSitemap(conf.sitemapUrl))
for i in xrange(len(targets)):
try:
target = targets[i]
if not re.search(r"(?i)\Ahttp[s]*://", target):
target = "http://%s" % target
crawl(target)
if conf.verbose in (1, 2):
@@ -466,20 +472,27 @@ def _findPageForms():
logger.info(infoMsg)
if not any((conf.bulkFile, conf.googleDork, conf.sitemapUrl)):
page, _, _ = Request.queryPage(content=True)
page, _, _ = Request.queryPage(content=True, ignoreSecondOrder=True)
if findPageForms(page, conf.url, True, True):
found = True
else:
if conf.bulkFile:
targets = getFileItems(conf.bulkFile)
elif conf.sitemapUrl:
targets = parseSitemap(conf.sitemapUrl)
targets = list(parseSitemap(conf.sitemapUrl))
elif conf.googleDork:
targets = [_[0] for _ in kb.targets]
kb.targets.clear()
else:
targets = []
for i in xrange(len(targets)):
try:
target = targets[i]
target = targets[i].strip()
if not re.search(r"(?i)\Ahttp[s]*://", target):
target = "http://%s" % target
page, _, _ = Request.getPage(url=target.strip(), cookie=conf.cookie, crawling=True, raise404=False)
if findPageForms(page, target, False, True):
found = True
@@ -1154,11 +1167,11 @@ def _setSafeVisit():
errMsg = "invalid format of a safe request file"
raise SqlmapSyntaxException(errMsg)
else:
if not re.search(r"\Ahttp[s]*://", conf.safeUrl):
if not re.search(r"(?i)\Ahttp[s]*://", conf.safeUrl):
if ":443/" in conf.safeUrl:
conf.safeUrl = "https://" + conf.safeUrl
conf.safeUrl = "https://%s" % conf.safeUrl
else:
conf.safeUrl = "http://" + conf.safeUrl
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"
@@ -1223,7 +1236,7 @@ def _setHTTPAuthentication():
elif not conf.authType and conf.authCred:
errMsg = "you specified the HTTP authentication credentials, "
errMsg += "but did not provide the type"
errMsg += "but did not provide the type (e.g. --auth-type=\"basic\")"
raise SqlmapSyntaxException(errMsg)
elif (conf.authType or "").lower() not in (AUTH_TYPE.BASIC, AUTH_TYPE.DIGEST, AUTH_TYPE.NTLM, AUTH_TYPE.PKI):
@@ -1273,8 +1286,8 @@ def _setHTTPAuthentication():
from ntlm import HTTPNtlmAuthHandler
except ImportError:
errMsg = "sqlmap requires Python NTLM third-party library "
errMsg += "in order to authenticate via NTLM, "
errMsg += "https://github.com/mullender/python-ntlm"
errMsg += "in order to authenticate via NTLM. Download from "
errMsg += "'https://github.com/mullender/python-ntlm'"
raise SqlmapMissingDependence(errMsg)
authHandler = HTTPNtlmAuthHandler.HTTPNtlmAuthHandler(kb.passwordMgr)
@@ -1302,6 +1315,9 @@ def _setHTTPExtraHeaders():
if header and value:
conf.httpHeaders.append((header, value))
elif headerValue.startswith('@'):
checkFile(headerValue[1:])
kb.headersFile = headerValue[1:]
else:
errMsg = "invalid header value: %s. Valid header format is 'name:value'" % repr(headerValue).lstrip('u')
raise SqlmapSyntaxException(errMsg)
@@ -1562,11 +1578,14 @@ def _cleanupOptions():
conf.testParameter = []
if conf.ignoreCode:
try:
conf.ignoreCode = [int(_) for _ in re.split(PARAMETER_SPLITTING_REGEX, conf.ignoreCode)]
except ValueError:
errMsg = "options '--ignore-code' should contain a list of integer values"
raise SqlmapSyntaxException(errMsg)
if conf.ignoreCode == IGNORE_CODE_WILDCARD:
conf.ignoreCode = xrange(0, 1000)
else:
try:
conf.ignoreCode = [int(_) for _ in re.split(PARAMETER_SPLITTING_REGEX, conf.ignoreCode)]
except ValueError:
errMsg = "options '--ignore-code' should contain a list of integer values or a wildcard value '%s'" % IGNORE_CODE_WILDCARD
raise SqlmapSyntaxException(errMsg)
else:
conf.ignoreCode = []
@@ -1814,7 +1833,6 @@ def _setConfAttributes():
conf.path = None
conf.port = None
conf.proxyList = None
conf.resultsFilename = None
conf.resultsFP = None
conf.scheme = None
conf.tests = []
@@ -1905,6 +1923,7 @@ def _setKnowledgeBaseAttributes(flushAll=True):
kb.forceWhere = None
kb.futileUnion = None
kb.heavilyDynamic = False
kb.headersFile = None
kb.headersFp = {}
kb.heuristicDbms = None
kb.heuristicExtendedDbms = None
@@ -1925,13 +1944,15 @@ def _setKnowledgeBaseAttributes(flushAll=True):
kb.lastParserStatus = None
kb.locks = AttribDict()
for _ in ("cache", "connError", "count", "handlers", "index", "io", "limit", "log", "socket", "redirect", "request", "value"):
for _ in ("cache", "connError", "count", "handlers", "hint", "index", "io", "limit", "log", "socket", "redirect", "request", "value"):
kb.locks[_] = threading.Lock()
kb.matchRatio = None
kb.maxConnectionsFlag = False
kb.mergeCookies = None
kb.multipleCtrlC = False
kb.negativeLogic = False
kb.normalizeCrawlingChoice = None
kb.nullConnection = None
kb.oldMsf = None
kb.orderByColumns = None
@@ -1999,6 +2020,7 @@ def _setKnowledgeBaseAttributes(flushAll=True):
kb.threadException = False
kb.tableExistsChoice = None
kb.uChar = NULL
kb.udfFail = False
kb.unionDuplicates = False
kb.wizardMode = False
kb.xpCmdshellAvailable = False
@@ -2197,6 +2219,13 @@ def _mergeOptions(inputOptions, overrideOptions):
if hasattr(conf, key) and conf[key] is None:
conf[key] = value
if conf.unstable:
if key in ("timeSec", "retries", "timeout"):
conf[key] *= 2
if conf.unstable:
conf.forcePartial = True
lut = {}
for group in optDict.keys():
lut.update((_.upper(), _) for _ in optDict[group])
@@ -2442,6 +2471,13 @@ def _basicOptionValidation():
errMsg = "invalid regular expression '%s' ('%s')" % (conf.regexp, getSafeExString(ex))
raise SqlmapSyntaxException(errMsg)
if conf.paramExclude:
try:
re.compile(conf.paramExclude)
except Exception as ex:
errMsg = "invalid regular expression '%s' ('%s')" % (conf.paramExclude, getSafeExString(ex))
raise SqlmapSyntaxException(errMsg)
if conf.crawlExclude:
try:
re.compile(conf.crawlExclude)
@@ -2485,6 +2521,10 @@ def _basicOptionValidation():
errMsg = "option '--csrf-url' requires usage of option '--csrf-token'"
raise SqlmapSyntaxException(errMsg)
if conf.csrfMethod and not conf.csrfToken:
errMsg = "option '--csrf-method' requires usage of option '--csrf-token'"
raise SqlmapSyntaxException(errMsg)
if conf.csrfToken and conf.threads > 1:
errMsg = "option '--csrf-url' is incompatible with option '--threads'"
raise SqlmapSyntaxException(errMsg)
+11 -8
View File
@@ -31,6 +31,7 @@ optDict = {
"loadCookies": "string",
"dropSetCookie": "boolean",
"agent": "string",
"mobile": "boolean",
"randomAgent": "boolean",
"host": "string",
"referer": "string",
@@ -60,6 +61,7 @@ optDict = {
"skipUrlEncode": "boolean",
"csrfToken": "string",
"csrfUrl": "string",
"csrfMethod": "string",
"forceSSL": "boolean",
"chunked": "boolean",
"hpp": "boolean",
@@ -100,6 +102,7 @@ optDict = {
"notString": "string",
"regexp": "string",
"code": "integer",
"smart": "boolean",
"textOnly": "boolean",
"titles": "boolean",
},
@@ -197,10 +200,12 @@ optDict = {
"General": {
"trafficFile": "string",
"answers": "string",
"batch": "boolean",
"binaryFields": "string",
"charset": "string",
"checkInternet": "boolean",
"cleanup": "boolean",
"crawlDepth": "integer",
"crawlExclude": "string",
"csvDel": "string",
@@ -210,6 +215,7 @@ optDict = {
"flushSession": "boolean",
"forms": "boolean",
"freshQueries": "boolean",
"googlePage": "integer",
"harFile": "string",
"hexConvert": "boolean",
"outputDir": "string",
@@ -218,27 +224,24 @@ optDict = {
"repair": "boolean",
"saveConfig": "string",
"scope": "string",
"skipWaf": "boolean",
"testFilter": "string",
"testSkip": "string",
"updateAll": "boolean",
"webRoot": "string",
},
"Miscellaneous": {
"alert": "string",
"answers": "string",
"beep": "boolean",
"cleanup": "boolean",
"dependencies": "boolean",
"disableColoring": "boolean",
"googlePage": "integer",
"listTampers": "boolean",
"mobile": "boolean",
"offline": "boolean",
"purge": "boolean",
"skipWaf": "boolean",
"smart": "boolean",
"resultsFile": "string",
"tmpDir": "string",
"webRoot": "string",
"unstable": "boolean",
"updateAll": "boolean",
"wizard": "boolean",
"verbose": "integer",
},
+2
View File
@@ -20,6 +20,7 @@ import thirdparty.chardet.universaldetector
from lib.core.common import filterNone
from lib.core.common import getSafeExString
from lib.core.common import isDigit
from lib.core.common import isListLike
from lib.core.common import readInput
from lib.core.common import shellExec
@@ -62,6 +63,7 @@ def resolveCrossReferences():
Place for cross-reference resolution
"""
lib.core.threads.isDigit = isDigit
lib.core.threads.readInput = readInput
lib.core.common.getPageTemplate = getPageTemplate
lib.core.convert.filterNone = filterNone
+1 -1
View File
@@ -7,13 +7,13 @@ See the file 'LICENSE' for copying permission
import sqlite3
from extra.safe2bin.safe2bin import safechardecode
from lib.core.common import getSafeExString
from lib.core.common import unsafeSQLIdentificatorNaming
from lib.core.exception import SqlmapConnectionException
from lib.core.exception import SqlmapGenericException
from lib.core.exception import SqlmapValueException
from lib.core.settings import UNICODE_ENCODING
from lib.utils.safe2bin import safechardecode
class Replication(object):
"""
+7 -2
View File
@@ -40,9 +40,14 @@ def getRevisionNumber():
with openFile(filePath, "r") as f:
content = getText(f.read())
filePath = None
if content.startswith("ref: "):
filePath = os.path.join(_, ".git", content.replace("ref: ", "")).strip()
else:
try:
filePath = os.path.join(_, ".git", content.replace("ref: ", "")).strip()
except UnicodeError:
pass
if filePath is None:
match = re.match(r"(?i)[0-9a-f]{32}", content)
retVal = match.group(0) if match else None
break
+11 -8
View File
@@ -18,7 +18,7 @@ from lib.core.enums import OS
from thirdparty.six import unichr as _unichr
# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
VERSION = "1.3.8.0"
VERSION = "1.3.11.0"
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)
@@ -240,7 +240,7 @@ IS_WIN = PLATFORM == "nt"
IS_TTY = os.isatty(sys.stdout.fileno())
# DBMS system databases
MSSQL_SYSTEM_DBS = ("Northwind", "master", "model", "msdb", "pubs", "tempdb")
MSSQL_SYSTEM_DBS = ("Northwind", "master", "model", "msdb", "pubs", "tempdb", "Resource", "ReportServer", "ReportServerTempDB")
MYSQL_SYSTEM_DBS = ("information_schema", "mysql", "performance_schema", "sys")
PGSQL_SYSTEM_DBS = ("information_schema", "pg_catalog", "pg_toast", "pgagent")
ORACLE_SYSTEM_DBS = ('ANONYMOUS', 'APEX_030200', 'APEX_PUBLIC_USER', 'APPQOSSYS', 'BI', 'CTXSYS', 'DBSNMP', 'DIP', 'EXFSYS', 'FLOWS_%', 'FLOWS_FILES', 'HR', 'IX', 'LBACSYS', 'MDDATA', 'MDSYS', 'MGMT_VIEW', 'OC', 'OE', 'OLAPSYS', 'ORACLE_OCM', 'ORDDATA', 'ORDPLUGINS', 'ORDSYS', 'OUTLN', 'OWBSYS', 'PM', 'SCOTT', 'SH', 'SI_INFORMTN_SCHEMA', 'SPATIAL_CSW_ADMIN_USR', 'SPATIAL_WFS_ADMIN_USR', 'SYS', 'SYSMAN', 'SYSTEM', 'WKPROXY', 'WKSYS', 'WK_TEST', 'WMSYS', 'XDB', 'XS$NULL')
@@ -251,7 +251,7 @@ MAXDB_SYSTEM_DBS = ("SYSINFO", "DOMAIN")
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",)
INFORMIX_SYSTEM_DBS = ("sysmaster", "sysutils", "sysuser", "sysadmin")
MSSQL_ALIASES = ("microsoft sql server", "mssqlserver", "mssql", "ms")
@@ -333,6 +333,9 @@ BLANK = "<blank>"
# String representation for current database
CURRENT_DB = "CD"
# String representation for current user
CURRENT_USER = "CU"
# Name of SQLite file used for storing session data
SESSION_SQLITE_FILE = "session.sqlite"
@@ -410,6 +413,9 @@ CANDIDATE_SENTENCE_MIN_LENGTH = 10
# Character used for marking injectable position inside provided data
CUSTOM_INJECTION_MARK_CHAR = '*'
# Wildcard value that can be used in option --ignore-code
IGNORE_CODE_WILDCARD = '*'
# Other way to declare injection position
INJECT_HERE_REGEX = r"(?i)%INJECT[_ ]?HERE%"
@@ -540,7 +546,7 @@ BRUTE_TABLE_EXISTS_TEMPLATE = "EXISTS(SELECT %d FROM %s)"
BRUTE_COLUMN_EXISTS_TEMPLATE = "EXISTS(SELECT %s FROM %s)"
# Data inside shellcodeexec to be filled with random string
SHELLCODEEXEC_RANDOM_STRING_MARKER = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
SHELLCODEEXEC_RANDOM_STRING_MARKER = b"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
# Period after last-update to start nagging about the old revision
LAST_UPDATE_NAGGING_DAYS = 60
@@ -718,7 +724,7 @@ MAX_HELP_OPTION_LENGTH = 18
MAX_CONNECT_RETRIES = 100
# Strings for detecting formatting errors
FORMAT_EXCEPTION_STRINGS = ("Type mismatch", "Error converting", "Please enter a", "Conversion failed", "String or binary data would be truncated", "Failed to convert", "unable to interpret text value", "Input string was not in a correct format", "System.FormatException", "java.lang.NumberFormatException", "ValueError: invalid literal", "TypeMismatchException", "CF_SQL_INTEGER", "CF_SQL_NUMERIC", " for CFSQLTYPE ", "cfqueryparam cfsqltype", "InvalidParamTypeException", "Invalid parameter type", "Attribute validation error for tag", "is not of type numeric", "<cfif Not IsNumeric(", "invalid input syntax for integer", "invalid input syntax for type", "invalid number", "character to number conversion error", "unable to interpret text value", "String was not recognized as a valid", "Convert.ToInt", "cannot be converted to a ", "InvalidDataException")
FORMAT_EXCEPTION_STRINGS = ("Type mismatch", "Error converting", "Please enter a", "Conversion failed", "String or binary data would be truncated", "Failed to convert", "unable to interpret text value", "Input string was not in a correct format", "System.FormatException", "java.lang.NumberFormatException", "ValueError: invalid literal", "TypeMismatchException", "CF_SQL_INTEGER", "CF_SQL_NUMERIC", " for CFSQLTYPE ", "cfqueryparam cfsqltype", "InvalidParamTypeException", "Invalid parameter type", "Attribute validation error for tag", "is not of type numeric", "<cfif Not IsNumeric(", "invalid input syntax for integer", "invalid input syntax for type", "invalid number", "character to number conversion error", "unable to interpret text value", "String was not recognized as a valid", "Convert.ToInt", "cannot be converted to a ", "InvalidDataException", "Arguments are of the wrong type")
# Regular expression used for extracting ASP.NET view state values
VIEWSTATE_REGEX = r'(?i)(?P<name>__VIEWSTATE[^"]*)[^>]+value="(?P<result>[^"]+)'
@@ -807,9 +813,6 @@ BRUTE_DOC_ROOT_PREFIXES = {
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%")
}
# Table prefix to use in "takeover" functionalities (i.e. auxiliary tables used by sqlmap at the vulnerable DBMS)
TAKEOVER_TABLE_PREFIX = "sqlmap"
# Suffixes used in brute force search for web server document root
BRUTE_DOC_ROOT_SUFFIXES = ("", "html", "htdocs", "httpdocs", "php", "public", "src", "site", "build", "web", "www", "data", "sites/all", "www/build")
+3
View File
@@ -13,6 +13,7 @@ import subprocess
import time
from lib.core.compat import buffer
from lib.core.convert import getBytes
from lib.core.settings import IS_WIN
if IS_WIN:
@@ -192,6 +193,8 @@ def send_all(p, data):
if not data:
return
data = getBytes(data)
while len(data):
sent = p.send(data)
if not isinstance(sent, int):
+20 -16
View File
@@ -125,8 +125,8 @@ def _setRequestParams():
return retVal
if kb.processUserMarks is None and kb.customInjectionMark in conf.data:
message = "custom injection marker ('%s') found in option " % kb.customInjectionMark
message += "'--data'. Do you want to process it? [Y/n/q] "
message = "custom injection marker ('%s') found in POST " % kb.customInjectionMark
message += "body. Do you want to process it? [Y/n/q] "
choice = readInput(message, default='Y').upper()
if choice == 'Q':
@@ -151,12 +151,13 @@ def _setRequestParams():
conf.data = re.sub(r'("(?P<name>[^"]+)"\s*:\s*".+?)"(?<!\\")', functools.partial(process, repl=r'\g<1>%s"' % kb.customInjectionMark), conf.data)
conf.data = re.sub(r'("(?P<name>[^"]+)"\s*:\s*)(-?\d[\d\.]*)\b', functools.partial(process, repl=r'\g<1>\g<3>%s' % kb.customInjectionMark), conf.data)
conf.data = re.sub(r'("(?P<name>[^"]+)"\s*:\s*)((true|false|null))\b', functools.partial(process, repl=r'\g<1>\g<3>%s' % kb.customInjectionMark), conf.data)
match = re.search(r'(?P<name>[^"]+)"\s*:\s*\[([^\]]+)\]', conf.data)
if match and not (conf.testParameter and match.group("name") not in conf.testParameter):
_ = match.group(2)
_ = re.sub(r'("[^"]+)"', r'\g<1>%s"' % kb.customInjectionMark, _)
_ = re.sub(r'(\A|,|\s+)(-?\d[\d\.]*\b)', r'\g<0>%s' % kb.customInjectionMark, _)
conf.data = conf.data.replace(match.group(0), match.group(0).replace(match.group(2), _))
for match in re.finditer(r'(?P<name>[^"]+)"\s*:\s*\[([^\]]+)\]', conf.data):
if not (conf.testParameter and match.group("name") not in conf.testParameter):
_ = match.group(2)
if kb.customInjectionMark not in _: # Note: only for unprocessed (simple) forms - i.e. non-associative arrays (e.g. [1,2,3])
_ = re.sub(r'("[^"]+)"', r'\g<1>%s"' % kb.customInjectionMark, _)
_ = re.sub(r'(\A|,|\s+)(-?\d[\d\.]*\b)', r'\g<0>%s' % kb.customInjectionMark, _)
conf.data = conf.data.replace(match.group(0), match.group(0).replace(match.group(2), _))
kb.postHint = POST_HINT.JSON
@@ -560,16 +561,18 @@ def _setResultsFile():
return
if not conf.resultsFP:
conf.resultsFilename = os.path.join(paths.SQLMAP_OUTPUT_PATH, time.strftime(RESULTS_FILE_FORMAT).lower())
conf.resultsFile = conf.resultsFile or os.path.join(paths.SQLMAP_OUTPUT_PATH, time.strftime(RESULTS_FILE_FORMAT).lower())
found = os.path.exists(conf.resultsFile)
try:
conf.resultsFP = openFile(conf.resultsFilename, "a", UNICODE_ENCODING, buffering=0)
conf.resultsFP = openFile(conf.resultsFile, "a", UNICODE_ENCODING, buffering=0)
except (OSError, IOError) as ex:
try:
warnMsg = "unable to create results file '%s' ('%s'). " % (conf.resultsFilename, getUnicode(ex))
handle, conf.resultsFilename = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.RESULTS, suffix=".csv")
warnMsg = "unable to create results file '%s' ('%s'). " % (conf.resultsFile, getUnicode(ex))
handle, conf.resultsFile = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.RESULTS, suffix=".csv")
os.close(handle)
conf.resultsFP = openFile(conf.resultsFilename, "w+", UNICODE_ENCODING, buffering=0)
warnMsg += "Using temporary file '%s' instead" % conf.resultsFilename
conf.resultsFP = openFile(conf.resultsFile, "w+", UNICODE_ENCODING, buffering=0)
warnMsg += "Using temporary file '%s' instead" % conf.resultsFile
logger.warn(warnMsg)
except IOError as _:
errMsg = "unable to write to the temporary directory ('%s'). " % _
@@ -578,9 +581,10 @@ def _setResultsFile():
errMsg += "create temporary files and/or directories"
raise SqlmapSystemException(errMsg)
conf.resultsFP.writelines("Target URL,Place,Parameter,Technique(s),Note(s)%s" % os.linesep)
if not found:
conf.resultsFP.writelines("Target URL,Place,Parameter,Technique(s),Note(s)%s" % os.linesep)
logger.info("using '%s' as the CSV results file in multiple targets mode" % conf.resultsFilename)
logger.info("using '%s' as the CSV results file in multiple targets mode" % conf.resultsFile)
def _createFilesDir():
"""
+1
View File
@@ -75,6 +75,7 @@ def vulnTest():
for options, checks in (
("--flush-session", ("CloudFlare",)),
("--flush-session --data='{\"id\": 1}' --banner", ("Payload: {\"id\"", "banner: '3")),
("--flush-session --parse-errors --eval=\"id2=2\" --referer=\"localhost\" --cookie=\"PHPSESSID=d41d8cd98f00b204e9800998ecf8427e\"", (": syntax error", "Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", "back-end DBMS: SQLite", "3 columns")),
("--banner --schema --dump -T users --binary-fields=surname --where \"id>3\"", ("banner: '3", "INTEGER", "TEXT", "id", "name", "surname", "2 entries", "6E616D6569736E756C6C")),
("--all --tamper=between,randomcase", ("5 entries", "luther", "blisset", "fluffy", "179ad45c6ce2cb97cf1029e212046e81", "NULL", "nameisnull", "testpass")),
+19 -9
View File
@@ -73,6 +73,10 @@ def readInput(message, default=None, checkBatch=True, boolean=False):
# It will be overwritten by original from lib.core.common
pass
def isDigit(value):
# It will be overwritten by original from lib.core.common
pass
def getCurrentThreadData():
"""
Returns current thread's local data
@@ -97,11 +101,11 @@ def exceptionHandledFunction(threadFunction, silent=False):
except Exception as ex:
from lib.core.common import getSafeExString
if not silent and kb.get("threadContinue"):
if not silent and kb.get("threadContinue") and not isinstance(ex, SqlmapUserQuitException):
errMsg = getSafeExString(ex) if isinstance(ex, SqlmapBaseException) else "%s: %s" % (type(ex).__name__, getSafeExString(ex))
logger.error("thread %s: '%s'" % (threading.currentThread().getName(), errMsg))
if conf.get("verbose") > 1 and not isinstance(ex, (SqlmapUserQuitException, SqlmapConnectionException)):
if conf.get("verbose") > 1 and not isinstance(ex, SqlmapConnectionException):
traceback.print_exc()
def setDaemon(thread):
@@ -114,6 +118,7 @@ def setDaemon(thread):
def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardException=True, threadChoice=False, startThreadMsg=True):
threads = []
kb.multipleCtrlC = False
kb.threadContinue = True
kb.threadException = False
kb.technique = ThreadData.technique
@@ -124,10 +129,12 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio
choice = readInput(message, default=str(numThreads))
if choice:
skipThreadCheck = False
if choice.endswith('!'):
choice = choice[:-1]
skipThreadCheck = True
if choice.isdigit():
if isDigit(choice):
if int(choice) > MAX_NUMBER_OF_THREADS and not skipThreadCheck:
errMsg = "maximum number of used threads is %d avoiding potential connection issues" % MAX_NUMBER_OF_THREADS
logger.critical(errMsg)
@@ -185,6 +192,7 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio
pass
except KeyboardInterrupt:
kb.multipleCtrlC = True
raise SqlmapThreadException("user aborted (Ctrl+C was pressed multiple times)")
if forwardException:
@@ -199,13 +207,15 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio
traceback.print_exc()
except:
from lib.core.common import unhandledExceptionMessage
print()
kb.threadException = True
errMsg = unhandledExceptionMessage()
logger.error("thread %s: %s" % (threading.currentThread().getName(), errMsg))
traceback.print_exc()
if not kb.multipleCtrlC:
from lib.core.common import unhandledExceptionMessage
kb.threadException = True
errMsg = unhandledExceptionMessage()
logger.error("thread %s: %s" % (threading.currentThread().getName(), errMsg))
traceback.print_exc()
finally:
kb.threadContinue = True
+1
View File
@@ -105,6 +105,7 @@ def update():
dataToStdout("\r[%s] [INFO] update in progress" % time.strftime("%X"))
output = ""
try:
process = subprocess.Popen("git checkout . && git pull %s HEAD" % GIT_REPOSITORY, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=paths.SQLMAP_ROOT_PATH)
pollProcess(process, True)
+1 -1
View File
@@ -42,7 +42,7 @@ class Wordlist(six.Iterator):
def adjust(self):
self.closeFP()
if self.index > len(self.filenames):
raise StopIteration
return # Note: https://stackoverflow.com/a/30217723 (PEP 479)
elif self.index == len(self.filenames):
self.iter = iter(self.custom)
else:
+63 -33
View File
@@ -44,6 +44,26 @@ finally:
def get_groups(parser):
return getattr(parser, "option_groups", None) or getattr(parser, "_action_groups")
def get_all_options(parser):
retVal = set()
for option in get_actions(parser):
if hasattr(option, "option_strings"):
retVal.update(option.option_strings)
else:
retVal.update(option._long_opts)
retVal.update(option._short_opts)
for group in get_groups(parser):
for option in get_actions(group):
if hasattr(option, "option_strings"):
retVal.update(option.option_strings)
else:
retVal.update(option._long_opts)
retVal.update(option._short_opts)
return retVal
from lib.core.common import checkOldOptions
from lib.core.common import checkSystemEncoding
from lib.core.common import dataToStdout
@@ -152,6 +172,9 @@ def cmdLineParser(argv=None):
request.add_argument("--user-agent", dest="agent",
help="HTTP User-Agent header value")
request.add_argument("--mobile", dest="mobile", action="store_true",
help="Imitate smartphone through HTTP User-Agent header")
request.add_argument("--random-agent", dest="randomAgent", action="store_true",
help="Use randomly selected HTTP User-Agent header value")
@@ -242,6 +265,9 @@ def cmdLineParser(argv=None):
request.add_argument("--csrf-url", dest="csrfUrl",
help="URL address to visit for extraction of anti-CSRF token")
request.add_argument("--csrf-method", dest="csrfMethod",
help="HTTP method to use during anti-CSRF token page visit")
request.add_argument("--force-ssl", dest="forceSSL", action="store_true",
help="Force usage of SSL/HTTPS")
@@ -344,6 +370,9 @@ def cmdLineParser(argv=None):
detection.add_argument("--code", dest="code", type=int,
help="HTTP code to match when query is evaluated to True")
detection.add_argument("--smart", dest="smart", action="store_true",
help="Perform thorough tests only if positive heuristic(s)")
detection.add_argument("--text-only", dest="textOnly", action="store_true",
help="Compare pages based only on the textual content")
@@ -585,6 +614,9 @@ def cmdLineParser(argv=None):
general.add_argument("-t", dest="trafficFile",
help="Log all HTTP traffic into a textual file")
general.add_argument("--answers", dest="answers",
help="Set predefined answers (e.g. \"quit=N,follow=N\")")
general.add_argument("--batch", dest="batch", action="store_true",
help="Never ask for user input, use the default behavior")
@@ -594,6 +626,9 @@ def cmdLineParser(argv=None):
general.add_argument("--check-internet", dest="checkInternet", action="store_true",
help="Check Internet connection before assessing the target")
general.add_argument("--cleanup", dest="cleanup", action="store_true",
help="Clean up the DBMS from sqlmap specific UDF and tables")
general.add_argument("--crawl", dest="crawlDepth", type=int,
help="Crawl the website starting from the target URL")
@@ -624,6 +659,9 @@ def cmdLineParser(argv=None):
general.add_argument("--fresh-queries", dest="freshQueries", action="store_true",
help="Ignore query results stored in session file")
general.add_argument("--gpage", dest="googlePage", type=int,
help="Use Google dork results from specified page number")
general.add_argument("--har", dest="harFile",
help="Log all HTTP traffic into a HAR file")
@@ -648,17 +686,23 @@ def cmdLineParser(argv=None):
general.add_argument("--scope", dest="scope",
help="Regexp to filter targets from provided proxy log")
general.add_argument("--skip-waf", dest="skipWaf", action="store_true",
help="Skip heuristic detection of WAF/IPS protection")
general.add_argument("--table-prefix", dest="tablePrefix",
help="Prefix used for temporary tables (default: \"%s\")" % defaults.tablePrefix)
general.add_argument("--test-filter", dest="testFilter",
help="Select tests by payloads and/or titles (e.g. ROW)")
general.add_argument("--test-skip", dest="testSkip",
help="Skip tests by payloads and/or titles (e.g. BENCHMARK)")
general.add_argument("--update", dest="updateAll", action="store_true",
help="Update sqlmap")
general.add_argument("--web-root", dest="webRoot",
help="Web server document root directory (e.g. \"/var/www\")")
# Miscellaneous options
miscellaneous = parser.add_argument_group("Miscellaneous")
miscellaneous = parser.add_argument_group("Miscellaneous", "These options do not fit into any other category")
miscellaneous.add_argument("-z", dest="mnemonics",
help="Use short mnemonics (e.g. \"flu,bat,ban,tec=EU\")")
@@ -666,41 +710,26 @@ def cmdLineParser(argv=None):
miscellaneous.add_argument("--alert", dest="alert",
help="Run host OS command(s) when SQL injection is found")
miscellaneous.add_argument("--answers", dest="answers",
help="Set predefined answers (e.g. \"quit=N,follow=N\")")
miscellaneous.add_argument("--beep", dest="beep", action="store_true",
help="Beep on question and/or when SQL injection is found")
miscellaneous.add_argument("--cleanup", dest="cleanup", action="store_true",
help="Clean up the DBMS from sqlmap specific UDF and tables")
miscellaneous.add_argument("--dependencies", dest="dependencies", action="store_true",
help="Check for missing (optional) sqlmap dependencies")
miscellaneous.add_argument("--disable-coloring", dest="disableColoring", action="store_true",
help="Disable console output coloring")
miscellaneous.add_argument("--gpage", dest="googlePage", type=int,
help="Use Google dork results from specified page number")
miscellaneous.add_argument("--list-tampers", dest="listTampers", action="store_true",
help="Display list of available tamper scripts")
miscellaneous.add_argument("--mobile", dest="mobile", action="store_true",
help="Imitate smartphone through HTTP User-Agent header")
miscellaneous.add_argument("--offline", dest="offline", action="store_true",
help="Work in offline mode (only use session data)")
miscellaneous.add_argument("--purge", dest="purge", action="store_true",
help="Safely remove all content from sqlmap data directory")
miscellaneous.add_argument("--skip-waf", dest="skipWaf", action="store_true",
help="Skip heuristic detection of WAF/IPS protection")
miscellaneous.add_argument("--smart", dest="smart", action="store_true",
help="Conduct thorough tests only if positive heuristic(s)")
miscellaneous.add_argument("--results-file", dest="resultsFile",
help="Location of CSV results file in multiple targets mode")
miscellaneous.add_argument("--sqlmap-shell", dest="sqlmapShell", action="store_true",
help="Prompt for an interactive sqlmap shell")
@@ -708,8 +737,11 @@ def cmdLineParser(argv=None):
miscellaneous.add_argument("--tmp-dir", dest="tmpDir",
help="Local directory for storing temporary files")
miscellaneous.add_argument("--web-root", dest="webRoot",
help="Web server document root directory (e.g. \"/var/www\")")
miscellaneous.add_argument("--unstable", dest="unstable", action="store_true",
help="Adjust options for unstable connections")
miscellaneous.add_argument("--update", dest="updateAll", action="store_true",
help="Update sqlmap")
miscellaneous.add_argument("--wizard", dest="wizard", action="store_true",
help="Simple wizard interface for beginner users")
@@ -838,18 +870,10 @@ def cmdLineParser(argv=None):
parser.usage = ""
cmdLineOptions.sqlmapShell = True
_ = ["x", "q", "exit", "quit", "clear"]
commands = set(("x", "q", "exit", "quit", "clear"))
commands.update(get_all_options(parser))
for option in get_actions(parser):
_.extend(option._long_opts)
_.extend(option._short_opts)
for group in get_groups(parser):
for option in get_actions(group):
_.extend(option._long_opts)
_.extend(option._short_opts)
autoCompletion(AUTOCOMPLETE_TYPE.SQLMAP, commands=_)
autoCompletion(AUTOCOMPLETE_TYPE.SQLMAP, commands=commands)
while True:
command = None
@@ -884,8 +908,11 @@ def cmdLineParser(argv=None):
raise SqlmapSyntaxException("something went wrong during command line parsing ('%s')" % getSafeExString(ex))
for i in xrange(len(argv)):
longOptions = set(re.findall(r"\-\-([^= ]+?)=", parser.format_help()))
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])
raise SystemExit
@@ -931,6 +958,9 @@ def cmdLineParser(argv=None):
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:
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:
+5 -3
View File
@@ -34,6 +34,7 @@ from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
from lib.core.decorators import cachedmethod
from lib.core.decorators import lockedmethod
from lib.core.dicts import HTML_ENTITIES
from lib.core.enums import DBMS
from lib.core.enums import HTTP_HEADER
@@ -57,6 +58,7 @@ from thirdparty.odict import OrderedDict
from thirdparty.six import unichr as _unichr
from thirdparty.six.moves import http_client as _http_client
@lockedmethod
def forgeHeaders(items=None, base=None):
"""
Prepare HTTP Cookie, HTTP User-Agent and HTTP Referer headers to use when performing
@@ -110,9 +112,9 @@ def forgeHeaders(items=None, base=None):
if conf.loadCookies:
conf.httpHeaders = filterNone((item if item[0] != HTTP_HEADER.COOKIE else None) for item in conf.httpHeaders)
elif kb.mergeCookies is None:
message = "you provided a HTTP %s header value. " % HTTP_HEADER.COOKIE
message += "The target URL provided its own cookies within "
message += "the HTTP %s header which intersect with yours. " % HTTP_HEADER.SET_COOKIE
message = "you provided a HTTP %s header value, while " % HTTP_HEADER.COOKIE
message += "target URL provides its own cookies within "
message += "HTTP %s header which intersect with yours. " % HTTP_HEADER.SET_COOKIE
message += "Do you want to merge them in further requests? [Y/n] "
kb.mergeCookies = readInput(message, default='Y', boolean=True)
+1
View File
@@ -12,6 +12,7 @@ class SmartHTTPBasicAuthHandler(_urllib.request.HTTPBasicAuthHandler):
Reference: http://selenic.com/hg/rev/6c51a5056020
Fix for a: http://bugs.python.org/issue8797
"""
def __init__(self, *args, **kwargs):
_urllib.request.HTTPBasicAuthHandler.__init__(self, *args, **kwargs)
self.retried_req = set()
+29 -22
View File
@@ -22,7 +22,6 @@ except ImportError:
class WebSocketException(Exception):
pass
from extra.safe2bin.safe2bin import safecharencode
from lib.core.agent import agent
from lib.core.common import asciifyUrl
from lib.core.common import calculateDeltaSeconds
@@ -42,6 +41,7 @@ from lib.core.common import getRequestHeader
from lib.core.common import getSafeExString
from lib.core.common import isMultiThreadMode
from lib.core.common import logHTTPTraffic
from lib.core.common import openFile
from lib.core.common import popValue
from lib.core.common import pushValue
from lib.core.common import randomizeParameterValue
@@ -60,6 +60,7 @@ from lib.core.common import wasLastResponseDelayed
from lib.core.compat import patchHeaders
from lib.core.compat import xrange
from lib.core.convert import getBytes
from lib.core.convert import getText
from lib.core.convert import getUnicode
from lib.core.data import conf
from lib.core.data import kb
@@ -123,6 +124,7 @@ from lib.request.basic import processResponse
from lib.request.comparison import comparison
from lib.request.direct import direct
from lib.request.methodrequest import MethodRequest
from lib.utils.safe2bin import safecharencode
from thirdparty import six
from thirdparty.odict import OrderedDict
from thirdparty.six import unichr as _unichr
@@ -426,6 +428,14 @@ class Connect(object):
if auxHeaders:
headers = forgeHeaders(auxHeaders, headers)
if kb.headersFile:
content = openFile(kb.headersFile, "rb").read()
for line in content.split("\n"):
line = getText(line.strip())
if ':' in line:
header, value = line.split(':', 1)
headers[header] = value
for key, value in list(headers.items()):
del headers[key]
if isinstance(value, six.string_types):
@@ -749,21 +759,17 @@ class Connect(object):
if kb.connErrorCounter >= MAX_CONSECUTIVE_CONNECTION_ERRORS and kb.connErrorChoice is None:
message = "there seems to be a continuous problem with connection to the target. "
message += "Are you sure that you want to continue "
message += "with further target testing? [y/N] "
message += "Are you sure that you want to continue? [y/N] "
kb.connErrorChoice = readInput(message, default='N', boolean=True)
if kb.connErrorChoice is not None:
if kb.connErrorChoice:
raise SqlmapConnectionException(warnMsg)
else:
raise SqlmapUserQuitException
if kb.connErrorChoice is False:
raise SqlmapUserQuitException
if "forcibly closed" in tbMsg:
logger.critical(warnMsg)
return None, None, None
elif ignoreTimeout and any(_ in tbMsg for _ in ("timed out", "IncompleteRead")):
elif ignoreTimeout and any(_ in tbMsg for _ in ("timed out", "IncompleteRead", "Interrupted system call")):
return None if not conf.ignoreTimeouts else "", None, None
elif threadData.retriesCount < conf.retries and not kb.threadException:
warnMsg += ". sqlmap is going to retry the request"
@@ -833,7 +839,7 @@ class Connect(object):
@staticmethod
@stackedmethod
def queryPage(value=None, place=None, content=False, getRatioValue=False, silent=False, method=None, timeBasedCompare=False, noteResponseTime=True, auxHeaders=None, response=False, raise404=None, removeReflection=True, disableTampering=False):
def queryPage(value=None, place=None, content=False, getRatioValue=False, silent=False, method=None, timeBasedCompare=False, noteResponseTime=True, auxHeaders=None, response=False, raise404=None, removeReflection=True, disableTampering=False, ignoreSecondOrder=False):
"""
This method calls a function to get the target URL page content
and returns its page ratio (0 <= ratio <= 1) or a boolean value
@@ -1033,7 +1039,7 @@ class Connect(object):
return retVal
token = AttribDict()
page, headers, code = Connect.getPage(url=conf.csrfUrl or conf.url, data=conf.data if conf.csrfUrl == conf.url else None, method=conf.method if conf.csrfUrl == conf.url else None, cookie=conf.parameters.get(PLACE.COOKIE), direct=True, silent=True, ua=conf.parameters.get(PLACE.USER_AGENT), referer=conf.parameters.get(PLACE.REFERER), host=conf.parameters.get(PLACE.HOST))
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=...')
match = re.search(r"(?i)<input[^>]+\bname=[\"']?(?P<name>%s)\b[^>]*\bvalue=[\"']?(?P<value>[^>'\"]*)" % conf.csrfToken, page or "", re.I)
@@ -1333,17 +1339,18 @@ class Connect(object):
warnMsg += "behavior in custom WAF/IPS solutions"
singleTimeWarnMessage(warnMsg)
if conf.secondUrl:
page, headers, code = Connect.getPage(url=conf.secondUrl, cookie=cookie, ua=ua, silent=silent, auxHeaders=auxHeaders, response=response, raise404=False, ignoreTimeout=timeBasedCompare, refreshing=True)
elif kb.secondReq and IPS_WAF_CHECK_PAYLOAD not in _urllib.parse.unquote(value or ""):
def _(value):
if kb.customInjectionMark in (value or ""):
if payload is None:
value = value.replace(kb.customInjectionMark, "")
else:
value = re.sub(r"\w*%s" % re.escape(kb.customInjectionMark), payload, value)
return value
page, headers, code = Connect.getPage(url=_(kb.secondReq[0]), post=_(kb.secondReq[2]), method=kb.secondReq[1], cookie=kb.secondReq[3], silent=silent, auxHeaders=dict(auxHeaders, **dict(kb.secondReq[4])), response=response, raise404=False, ignoreTimeout=timeBasedCompare, refreshing=True)
if not ignoreSecondOrder:
if conf.secondUrl:
page, headers, code = Connect.getPage(url=conf.secondUrl, cookie=cookie, ua=ua, silent=silent, auxHeaders=auxHeaders, response=response, raise404=False, ignoreTimeout=timeBasedCompare, refreshing=True)
elif kb.secondReq and IPS_WAF_CHECK_PAYLOAD not in _urllib.parse.unquote(value or ""):
def _(value):
if kb.customInjectionMark in (value or ""):
if payload is None:
value = value.replace(kb.customInjectionMark, "")
else:
value = re.sub(r"\w*%s" % re.escape(kb.customInjectionMark), payload, value)
return value
page, headers, code = Connect.getPage(url=_(kb.secondReq[0]), post=_(kb.secondReq[2]), method=kb.secondReq[1], cookie=kb.secondReq[3], silent=silent, auxHeaders=dict(auxHeaders, **dict(kb.secondReq[4])), response=response, raise404=False, ignoreTimeout=timeBasedCompare, refreshing=True)
threadData.lastQueryDuration = calculateDeltaSeconds(start)
+2 -3
View File
@@ -7,7 +7,6 @@ See the file 'LICENSE' for copying permission
import time
from extra.safe2bin.safe2bin import safecharencode
from lib.core.agent import agent
from lib.core.common import Backend
from lib.core.common import calculateDeltaSeconds
@@ -25,8 +24,8 @@ from lib.core.enums import CUSTOM_LOGGING
from lib.core.enums import DBMS
from lib.core.enums import EXPECTED
from lib.core.enums import TIMEOUT_STATE
from lib.core.settings import TAKEOVER_TABLE_PREFIX
from lib.core.settings import UNICODE_ENCODING
from lib.utils.safe2bin import safecharencode
from lib.utils.timeout import timeout
def direct(query, content=True):
@@ -54,7 +53,7 @@ def direct(query, content=True):
if not select and "EXEC " not in query.upper():
timeout(func=conf.dbmsConnector.execute, args=(query,), duration=conf.timeout, default=None)
elif not (output and ("%soutput" % TAKEOVER_TABLE_PREFIX) not in query and ("%sfile" % TAKEOVER_TABLE_PREFIX) not in query):
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)
if state == TIMEOUT_STATE.NORMAL:
hashDBWrite(query, output, True)
+4 -3
View File
@@ -24,6 +24,7 @@ from lib.core.common import getTechniqueData
from lib.core.common import hashDBRetrieve
from lib.core.common import hashDBWrite
from lib.core.common import initTechnique
from lib.core.common import isDigit
from lib.core.common import isNoneValue
from lib.core.common import isNumPosStrValue
from lib.core.common import isTechniqueAvailable
@@ -235,7 +236,7 @@ def _goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, char
elif choice == 'Q':
raise SqlmapUserQuitException
elif choice.isdigit() and int(choice) > 0 and int(choice) <= count:
elif isDigit(choice) and int(choice) > 0 and int(choice) <= count:
stopLimit = int(choice)
infoMsg = "sqlmap is now going to retrieve the "
@@ -246,7 +247,7 @@ def _goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, char
message = "how many? "
stopLimit = readInput(message, default="10")
if not stopLimit.isdigit():
if not isDigit(stopLimit):
errMsg = "invalid choice"
logger.error(errMsg)
@@ -261,7 +262,7 @@ def _goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, char
return None
elif count and not count.isdigit():
elif count and not isDigit(count):
warnMsg = "it was not possible to count the number "
warnMsg += "of entries for the SQL query provided. "
warnMsg += "sqlmap will assume that it returns only "
+5 -4
View File
@@ -9,7 +9,6 @@ from __future__ import print_function
import sys
from extra.safe2bin.safe2bin import safechardecode
from lib.core.common import Backend
from lib.core.common import dataToStdout
from lib.core.common import getSQLSnippet
@@ -17,6 +16,7 @@ from lib.core.common import isStackingAvailable
from lib.core.common import readInput
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.enums import AUTOCOMPLETE_TYPE
from lib.core.enums import DBMS
@@ -28,6 +28,7 @@ from lib.request import inject
from lib.takeover.udf import UDF
from lib.takeover.web import Web
from lib.takeover.xp_cmdshell import XP_cmdshell
from lib.utils.safe2bin import safechardecode
from thirdparty.six.moves import input as _input
class Abstraction(Web, UDF, XP_cmdshell):
@@ -48,7 +49,7 @@ class Abstraction(Web, UDF, XP_cmdshell):
if Backend.isDbms(DBMS.PGSQL) and self.checkCopyExec():
self.copyExecCmd(cmd)
elif self.webBackdoorUrl and not isStackingAvailable():
elif self.webBackdoorUrl and (not isStackingAvailable() or kb.udfFail):
self.webBackdoorRunCmd(cmd)
elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL):
@@ -67,7 +68,7 @@ class Abstraction(Web, UDF, XP_cmdshell):
if Backend.isDbms(DBMS.PGSQL) and self.checkCopyExec():
retVal = self.copyExecCmd(cmd)
elif self.webBackdoorUrl and not isStackingAvailable():
elif self.webBackdoorUrl and (not isStackingAvailable() or kb.udfFail):
retVal = self.webBackdoorRunCmd(cmd)
elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL):
@@ -104,7 +105,7 @@ class Abstraction(Web, UDF, XP_cmdshell):
self.execCmd(cmd)
def shell(self):
if self.webBackdoorUrl and not isStackingAvailable():
if self.webBackdoorUrl and (not isStackingAvailable() or kb.udfFail):
infoMsg = "calling OS shell. To quit type "
infoMsg += "'x' or 'q' and press ENTER"
logger.info(infoMsg)
+6 -8
View File
@@ -23,6 +23,7 @@ from lib.core.common import dataToStdout
from lib.core.common import Backend
from lib.core.common import getLocalIP
from lib.core.common import getRemoteIP
from lib.core.common import isDigit
from lib.core.common import normalizePath
from lib.core.common import ntToPosixSlashes
from lib.core.common import pollProcess
@@ -154,7 +155,7 @@ class Metasploit(object):
choice = readInput(message, default="%d" % default)
if not choice or not choice.isdigit() or int(choice) > maxValue or int(choice) < 1:
if not choice or not isDigit(choice) or int(choice) > maxValue or int(choice) < 1:
choice = default
choice = int(choice)
@@ -227,24 +228,21 @@ class Metasploit(object):
if not choice or choice == "2":
_payloadStr = "windows/meterpreter"
break
elif choice == "3":
_payloadStr = "windows/shell"
break
elif choice == "1":
if Backend.isDbms(DBMS.PGSQL):
logger.warn("beware that the VNC injection might not work")
break
elif Backend.isDbms(DBMS.MSSQL) and Backend.isVersionWithin(("2005", "2008")):
break
elif not choice.isdigit():
elif not isDigit(choice):
logger.warn("invalid value, only digits are allowed")
elif int(choice) < 1 or int(choice) > 2:
@@ -634,7 +632,7 @@ class Metasploit(object):
self.shellcodeexecLocal = os.path.join(self.shellcodeexecLocal, "windows", "shellcodeexec.x%s.exe_" % "32")
content = decloak(self.shellcodeexecLocal)
if SHELLCODEEXEC_RANDOM_STRING_MARKER in content:
content = content.replace(SHELLCODEEXEC_RANDOM_STRING_MARKER, randomStr(len(SHELLCODEEXEC_RANDOM_STRING_MARKER)))
content = content.replace(SHELLCODEEXEC_RANDOM_STRING_MARKER, getBytes(randomStr(len(SHELLCODEEXEC_RANDOM_STRING_MARKER))))
_ = cloak(data=content)
handle, self.shellcodeexecLocal = tempfile.mkstemp(suffix="%s.exe_" % "32")
os.close(handle)
@@ -695,9 +693,9 @@ class Metasploit(object):
self._runMsfCliSmbrelay()
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL):
self.uncPath = "\\\\\\\\%s\\\\%s" % (self.lhostStr, self._randFile)
self.uncPath = r"\\\\%s\\%s" % (self.lhostStr, self._randFile)
else:
self.uncPath = "\\\\%s\\%s" % (self.lhostStr, self._randFile)
self.uncPath = r"\\%s\%s" % (self.lhostStr, self._randFile)
debugMsg = "Metasploit Framework console exited with return "
debugMsg += "code %s" % self._controlMsfCmd(self._msfCliProc, self.uncPathRequest)
+2 -3
View File
@@ -11,6 +11,7 @@ from lib.core.agent import agent
from lib.core.common import Backend
from lib.core.common import checkFile
from lib.core.common import dataToStdout
from lib.core.common import isDigit
from lib.core.common import isStackingAvailable
from lib.core.common import readInput
from lib.core.common import unArrayizeValue
@@ -339,11 +340,9 @@ class UDF(object):
if choice == 'Q':
break
elif hasattr(choice, "isdigit") and choice.isdigit() and int(choice) > 0 and int(choice) <= len(udfList):
elif isDigit(choice) and int(choice) > 0 and int(choice) <= len(udfList):
choice = int(choice)
break
elif isinstance(choice, int) and choice > 0 and choice <= len(udfList):
break
else:
warnMsg = "invalid value, only digits >= 1 and "
warnMsg += "<= %d are allowed" % len(udfList)
+2 -1
View File
@@ -22,6 +22,7 @@ from lib.core.common import getPublicTypeMembers
from lib.core.common import getSQLSnippet
from lib.core.common import getTechnique
from lib.core.common import getTechniqueData
from lib.core.common import isDigit
from lib.core.common import isTechniqueAvailable
from lib.core.common import isWindowsDriveLetterPath
from lib.core.common import normalizePath
@@ -200,7 +201,7 @@ class Web(object):
while True:
choice = readInput(message, default=str(default))
if not choice.isdigit():
if not isDigit(choice):
logger.warn("invalid value, only digits are allowed")
elif int(choice) < 1 or int(choice) > len(choices):
+3 -6
View File
@@ -8,10 +8,8 @@ See the file 'LICENSE' for copying permission
from __future__ import division
import re
import threading
import time
from extra.safe2bin.safe2bin import safecharencode
from lib.core.agent import agent
from lib.core.common import Backend
from lib.core.common import calculateDeltaSeconds
@@ -59,6 +57,7 @@ from lib.core.threads import runThreads
from lib.core.unescaper import unescaper
from lib.request.connect import Connect as Request
from lib.utils.progress import ProgressBar
from lib.utils.safe2bin import safecharencode
from lib.utils.xrange import xrange
def bisection(payload, expression, length=None, charsetType=None, firstChar=None, lastChar=None, dump=False):
@@ -190,10 +189,8 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
else:
dataToStdout("\r[%s] [INFO] retrieved: " % time.strftime("%X"))
hintlock = threading.Lock()
def tryHint(idx):
with hintlock:
with kb.locks.hint:
hintValue = kb.hintValue
if payload is not None and len(hintValue or "") > 0 and len(hintValue) >= idx:
@@ -212,7 +209,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
if result:
return hintValue[idx - 1]
with hintlock:
with kb.locks.hint:
kb.hintValue = ""
return None
+1 -1
View File
@@ -8,7 +8,6 @@ See the file 'LICENSE' for copying permission
import re
import time
from extra.safe2bin.safe2bin import safecharencode
from lib.core.agent import agent
from lib.core.common import Backend
from lib.core.common import calculateDeltaSeconds
@@ -33,6 +32,7 @@ from lib.core.settings import MAX_DNS_LABEL
from lib.core.settings import PARTIAL_VALUE_MARKER
from lib.core.unescaper import unescaper
from lib.request.connect import Connect as Request
from lib.utils.safe2bin import safecharencode
def dnsUse(payload, expression):
"""
+5 -5
View File
@@ -10,7 +10,6 @@ from __future__ import print_function
import re
import time
from extra.safe2bin.safe2bin import safecharencode
from lib.core.agent import agent
from lib.core.bigarray import BigArray
from lib.core.common import Backend
@@ -60,6 +59,7 @@ from lib.core.threads import runThreads
from lib.core.unescaper import unescaper
from lib.request.connect import Connect as Request
from lib.utils.progress import ProgressBar
from lib.utils.safe2bin import safecharencode
from thirdparty import six
def _oneShotErrorUse(expression, field=None, chunkTest=False):
@@ -76,7 +76,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False):
threadData.resumed = retVal is not None and not partialValue
if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL, DBMS.ORACLE)) and kb.errorChunkLength is None and not chunkTest and not kb.testMode:
if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL, DBMS.SYBASE, DBMS.ORACLE)) and kb.errorChunkLength is None and not chunkTest and not kb.testMode:
debugMsg = "searching for error chunk length..."
logger.debug(debugMsg)
@@ -117,7 +117,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False):
if field:
nulledCastedField = agent.nullAndCastField(field)
if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL, DBMS.ORACLE)) and not any(_ in field for _ in ("COUNT", "CASE")) and kb.errorChunkLength and not chunkTest:
if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL, DBMS.SYBASE, DBMS.ORACLE)) and not any(_ in field for _ in ("COUNT", "CASE")) and kb.errorChunkLength and not chunkTest:
extendedField = re.search(r"[^ ,]*%s[^ ,]*" % re.escape(field), expression).group(0)
if extendedField != field: # e.g. MIN(surname)
nulledCastedField = extendedField.replace(field, nulledCastedField)
@@ -177,7 +177,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False):
else:
output = output.rstrip()
if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL, DBMS.ORACLE)):
if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL, DBMS.SYBASE, DBMS.ORACLE)):
if offset == 1:
retVal = output
else:
@@ -367,7 +367,7 @@ def errorUse(expression, dump=False):
message = "due to huge table size do you want to remove "
message += "ORDER BY clause gaining speed over consistency? [y/N] "
if readInput(message, default="N", boolean=True):
if readInput(message, default='N', boolean=True):
expression = expression[:expression.index(" ORDER BY ")]
numThreads = min(conf.threads, (stopLimit - startLimit))
+2 -2
View File
@@ -163,7 +163,7 @@ def _findUnionCharCount(comment, place, parameter, value, prefix, suffix, where=
if retVal:
infoMsg = "target URL appears to be UNION injectable with %d columns" % retVal
singleTimeLogMessage(infoMsg, logging.INFO, re.sub(r"\d+", "N", infoMsg))
singleTimeLogMessage(infoMsg, logging.INFO, re.sub(r"\d+", 'N', infoMsg))
return retVal
@@ -290,7 +290,7 @@ def _unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix)
if not conf.uChar and count > 1 and kb.uChar == NULL:
message = "injection not exploitable with NULL values. Do you want to try with a random integer value for option '--union-char'? [Y/n] "
if not readInput(message, default="Y", boolean=True):
if not readInput(message, default='Y', boolean=True):
warnMsg += "usage of option '--union-char' "
warnMsg += "(e.g. '--union-char=1') "
else:
+2 -2
View File
@@ -10,7 +10,6 @@ import re
import time
import xml.etree.ElementTree
from extra.safe2bin.safe2bin import safecharencode
from lib.core.agent import agent
from lib.core.bigarray import BigArray
from lib.core.common import arrayizeValue
@@ -62,6 +61,7 @@ from lib.core.threads import runThreads
from lib.core.unescaper import unescaper
from lib.request.connect import Connect as Request
from lib.utils.progress import ProgressBar
from lib.utils.safe2bin import safecharencode
from thirdparty import six
from thirdparty.odict import OrderedDict
@@ -123,7 +123,7 @@ def _oneShotUnionUse(expression, unpack=True, limited=False):
try:
decodeBase64(value)
except binascii.Error:
except (binascii.Error, TypeError):
base64 = False
break
+67 -56
View File
@@ -60,6 +60,7 @@ def _addPageTextWords():
return wordsList
@stackedmethod
def tableExists(tableFile, regex=None):
if kb.tableExistsChoice is None and not any(_ for _ in kb.injection.data if _ not in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)) and not conf.direct:
warnMsg = "it's not recommended to use '%s' and/or '%s' " % (PAYLOAD.SQLINJECTION[PAYLOAD.TECHNIQUE.TIME], PAYLOAD.SQLINJECTION[PAYLOAD.TECHNIQUE.STACKED])
@@ -74,15 +75,17 @@ def tableExists(tableFile, regex=None):
result = inject.checkBooleanExpression("%s" % safeStringFormat(BRUTE_TABLE_EXISTS_TEMPLATE, (randomInt(1), randomStr())))
if conf.db and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2):
conf.db = conf.db.upper()
if result:
errMsg = "can't use table existence check because of detected invalid results "
errMsg += "(most likely caused by inability of the used injection "
errMsg += "to distinguish erroneous results)"
raise SqlmapDataException(errMsg)
pushValue(conf.db)
if conf.db and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2):
conf.db = conf.db.upper()
message = "which common tables (wordlist) file do you want to use?\n"
message += "[1] default '%s' (press Enter)\n" % tableFile
message += "[2] custom"
@@ -92,80 +95,88 @@ def tableExists(tableFile, regex=None):
message = "what's the custom common tables file location?\n"
tableFile = readInput(message) or tableFile
infoMsg = "checking table existence using items from '%s'" % tableFile
infoMsg = "performing table existence using items from '%s'" % tableFile
logger.info(infoMsg)
tables = getFileItems(tableFile, lowercase=Backend.getIdentifiedDbms() in (DBMS.ACCESS,), unique=True)
tables.extend(_addPageTextWords())
tables = filterListValue(tables, regex)
threadData = getCurrentThreadData()
threadData.shared.count = 0
threadData.shared.limit = len(tables)
threadData.shared.files = []
threadData.shared.unique = set()
for conf.db in (conf.db.split(',') if conf.db else [conf.db]):
if conf.db:
infoMsg = "checking database '%s'" % conf.db
logger.info(infoMsg)
def tableExistsThread():
threadData = getCurrentThreadData()
threadData.shared.count = 0
threadData.shared.limit = len(tables)
threadData.shared.files = []
threadData.shared.unique = set()
while kb.threadContinue:
kb.locks.count.acquire()
if threadData.shared.count < threadData.shared.limit:
table = safeSQLIdentificatorNaming(tables[threadData.shared.count], True)
threadData.shared.count += 1
kb.locks.count.release()
else:
kb.locks.count.release()
break
def tableExistsThread():
threadData = getCurrentThreadData()
if conf.db and METADB_SUFFIX not in conf.db and Backend.getIdentifiedDbms() not in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD):
fullTableName = "%s.%s" % (conf.db, table)
else:
fullTableName = table
while kb.threadContinue:
kb.locks.count.acquire()
if threadData.shared.count < threadData.shared.limit:
table = safeSQLIdentificatorNaming(tables[threadData.shared.count], True)
threadData.shared.count += 1
kb.locks.count.release()
else:
kb.locks.count.release()
break
result = inject.checkBooleanExpression("%s" % safeStringFormat(BRUTE_TABLE_EXISTS_TEMPLATE, (randomInt(1), fullTableName)))
if conf.db and METADB_SUFFIX not in conf.db and Backend.getIdentifiedDbms() not in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD):
fullTableName = "%s.%s" % (conf.db, table)
else:
fullTableName = table
kb.locks.io.acquire()
result = inject.checkBooleanExpression("%s" % safeStringFormat(BRUTE_TABLE_EXISTS_TEMPLATE, (randomInt(1), fullTableName)))
if result and table.lower() not in threadData.shared.unique:
threadData.shared.files.append(table)
threadData.shared.unique.add(table.lower())
kb.locks.io.acquire()
if conf.verbose in (1, 2) and not conf.api:
clearConsoleLine(True)
infoMsg = "[%s] [INFO] retrieved: %s\n" % (time.strftime("%X"), unsafeSQLIdentificatorNaming(table))
dataToStdout(infoMsg, True)
if result and table.lower() not in threadData.shared.unique:
threadData.shared.files.append(table)
threadData.shared.unique.add(table.lower())
if conf.verbose in (1, 2):
status = '%d/%d items (%d%%)' % (threadData.shared.count, threadData.shared.limit, round(100.0 * threadData.shared.count / threadData.shared.limit))
dataToStdout("\r[%s] [INFO] tried %s" % (time.strftime("%X"), status), True)
if conf.verbose in (1, 2) and not conf.api:
clearConsoleLine(True)
infoMsg = "[%s] [INFO] retrieved: %s\n" % (time.strftime("%X"), unsafeSQLIdentificatorNaming(table))
dataToStdout(infoMsg, True)
kb.locks.io.release()
if conf.verbose in (1, 2):
status = '%d/%d items (%d%%)' % (threadData.shared.count, threadData.shared.limit, round(100.0 * threadData.shared.count / threadData.shared.limit))
dataToStdout("\r[%s] [INFO] tried %s" % (time.strftime("%X"), status), True)
try:
runThreads(conf.threads, tableExistsThread, threadChoice=True)
except KeyboardInterrupt:
warnMsg = "user aborted during table existence "
warnMsg += "check. sqlmap will display partial output"
logger.warn(warnMsg)
kb.locks.io.release()
clearConsoleLine(True)
dataToStdout("\n")
try:
runThreads(conf.threads, tableExistsThread, threadChoice=True)
except KeyboardInterrupt:
warnMsg = "user aborted during table existence "
warnMsg += "check. sqlmap will display partial output"
logger.warn(warnMsg)
if not threadData.shared.files:
warnMsg = "no table(s) found"
logger.warn(warnMsg)
else:
for item in threadData.shared.files:
if conf.db not in kb.data.cachedTables:
kb.data.cachedTables[conf.db] = [item]
else:
kb.data.cachedTables[conf.db].append(item)
clearConsoleLine(True)
dataToStdout("\n")
for _ in ((conf.db, item) for item in threadData.shared.files):
if _ not in kb.brute.tables:
kb.brute.tables.append(_)
if not threadData.shared.files:
warnMsg = "no table(s) found"
if conf.db:
warnMsg += " for database '%s'" % conf.db
logger.warn(warnMsg)
else:
for item in threadData.shared.files:
if conf.db not in kb.data.cachedTables:
kb.data.cachedTables[conf.db] = [item]
else:
kb.data.cachedTables[conf.db].append(item)
for _ in ((conf.db, item) for item in threadData.shared.files):
if _ not in kb.brute.tables:
kb.brute.tables.append(_)
conf.db = popValue()
hashDBWrite(HASHDB_KEYS.KB_BRUTE_TABLES, kb.brute.tables, True)
return kb.data.cachedTables
+23 -3
View File
@@ -15,6 +15,7 @@ import time
from lib.core.common import checkSameHost
from lib.core.common import clearConsoleLine
from lib.core.common import dataToStdout
from lib.core.common import extractRegexResult
from lib.core.common import findPageForms
from lib.core.common import getSafeExString
from lib.core.common import openFile
@@ -92,8 +93,7 @@ def crawl(target):
soup = BeautifulSoup(content)
tags = soup('a')
if not tags:
tags = re.finditer(r'(?i)<a[^>]+href=["\'](?P<href>[^>"\']+)', content)
tags += re.finditer(r'(?i)\s(href|src)=["\'](?P<href>[^>"\']+)', content)
for tag in tags:
href = tag.get("href") if hasattr(tag, "get") else tag.group("href")
@@ -112,7 +112,7 @@ def crawl(target):
elif not _:
continue
if url.split('.')[-1].lower() not in CRAWL_EXCLUDE_EXTENSIONS:
if (extractRegexResult(r"\A[^?]+\.(?P<result>\w+)(\?|\Z)", url) or "").lower() not in CRAWL_EXCLUDE_EXTENSIONS:
with kb.locks.value:
threadData.shared.deeper.add(url)
if re.search(r"(.*?)\?(.+)", url):
@@ -195,6 +195,26 @@ def crawl(target):
for url in threadData.shared.value:
kb.targets.add((urldecode(url, kb.pageEncoding), None, None, None, None))
if kb.normalizeCrawlingChoice is None:
message = "do you want to normalize "
message += "crawling results [Y/n] "
kb.normalizeCrawlingChoice = readInput(message, default='Y', boolean=True)
if kb.normalizeCrawlingChoice:
seen = set()
results = OrderedSet()
for target in kb.targets:
match = re.search(r"/[^/?]*\?.*\Z", target[0])
if match:
key = re.sub(r"=[^=&]*", "=", match.group(0))
if key not in seen:
results.add(target)
seen.add(key)
kb.targets = results
storeResultsToFile(kb.targets)
def storeResultsToFile(results):
+36 -19
View File
@@ -33,6 +33,7 @@ else:
import base64
import binascii
import gc
import hashlib
import os
import re
import tempfile
@@ -155,7 +156,24 @@ def postgres_passwd(password, username, uppercase=False):
return retVal.upper() if uppercase else retVal.lower()
def mssql_passwd(password, salt, uppercase=False):
def mssql_new_passwd(password, salt, uppercase=False): # since version '2012'
"""
Reference(s):
http://hashcat.net/forum/thread-1474.html
https://sqlity.net/en/2460/sql-password-hash/
>>> mssql_new_passwd(password='testpass', salt='4086ceb6', uppercase=False)
'0x02004086ceb6eb051cdbc5bdae68ffc66c918d4977e592f6bdfc2b444a7214f71fa31c35902c5b7ae773ed5f4c50676d329120ace32ee6bc81c24f70711eb0fc6400e85ebf25'
"""
binsalt = decodeHex(salt)
unistr = b"".join((_.encode(UNICODE_ENCODING) + b"\0") if ord(_) < 256 else _.encode(UNICODE_ENCODING) for _ in password)
retVal = "0200%s%s" % (salt, sha512(unistr + binsalt).hexdigest())
return "0x%s" % (retVal.upper() if uppercase else retVal.lower())
def mssql_passwd(password, salt, uppercase=False): # versions '2005' and '2008'
"""
Reference(s):
http://www.leidecker.info/projects/phrasendrescher/mssql.c
@@ -172,7 +190,7 @@ def mssql_passwd(password, salt, uppercase=False):
return "0x%s" % (retVal.upper() if uppercase else retVal.lower())
def mssql_old_passwd(password, salt, uppercase=True): # prior to version '2005'
def mssql_old_passwd(password, salt, uppercase=True): # version '2000' and before
"""
Reference(s):
www.exploit-db.com/download_pdf/15537/
@@ -190,22 +208,6 @@ def mssql_old_passwd(password, salt, uppercase=True): # prior to version '2005'
return "0x%s" % (retVal.upper() if uppercase else retVal.lower())
def mssql_new_passwd(password, salt, uppercase=False):
"""
Reference(s):
http://hashcat.net/forum/thread-1474.html
>>> mssql_new_passwd(password='testpass', salt='4086ceb6', uppercase=False)
'0x02004086ceb6eb051cdbc5bdae68ffc66c918d4977e592f6bdfc2b444a7214f71fa31c35902c5b7ae773ed5f4c50676d329120ace32ee6bc81c24f70711eb0fc6400e85ebf25'
"""
binsalt = decodeHex(salt)
unistr = b"".join((_.encode(UNICODE_ENCODING) + b"\0") if ord(_) < 256 else _.encode(UNICODE_ENCODING) for _ in password)
retVal = "0200%s%s" % (salt, sha512(unistr + binsalt).hexdigest())
return "0x%s" % (retVal.upper() if uppercase else retVal.lower())
def oracle_passwd(password, salt, uppercase=True):
"""
Reference(s):
@@ -246,6 +248,18 @@ 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)
@@ -708,6 +722,7 @@ def attackDumpedTable():
if hash_:
key = hash_ if hash_ not in replacements else replacements[hash_]
lut[key.lower()] = password
lut["0x%s" % key.lower()] = password
debugMsg = "post-processing table dump"
logger.debug(debugMsg)
@@ -942,6 +957,8 @@ def dictionaryAttack(attack_dict):
if hash_regex in (HASH.MD5_BASE64, HASH.SHA1_BASE64, HASH.SHA256_BASE64, HASH.SHA512_BASE64):
item = [(user, encodeHex(decodeBase64(hash_, binary=True))), {}]
elif hash_regex in (HASH.MYSQL, HASH.MYSQL_OLD, HASH.MD5_GENERIC, HASH.SHA1_GENERIC, HASH.SHA224_GENERIC, HASH.SHA256_GENERIC, HASH.SHA384_GENERIC, HASH.SHA512_GENERIC, HASH.APACHE_SHA1):
if hash_.startswith("0x"): # Reference: https://docs.microsoft.com/en-us/sql/t-sql/functions/hashbytes-transact-sql?view=sql-server-2017
hash_ = hash_[2:]
item = [(user, hash_), {}]
elif hash_regex in (HASH.SSHA,):
item = [(user, hash_), {"salt": decodeBase64(hash_, binary=True)[20:]}]
@@ -983,7 +1000,7 @@ def dictionaryAttack(attack_dict):
resumes.append((user, hash_, resumed))
keys.add(hash_)
except (binascii.Error, IndexError):
except (binascii.Error, TypeError, IndexError):
pass
if not attack_info:
+1 -1
View File
@@ -78,7 +78,7 @@ class ReqHandler(_BaseHTTPServer.BaseHTTPRequestHandler):
self.send_header(HTTP_HEADER.CONNECTION, "close")
if content is not None:
for match in re.finditer(b"<\!(\w+)\!>", content):
for match in re.finditer(b"<!(\\w+)!>", content):
name = match.group(1)
_ = getattr(self, "_%s" % name.lower(), None)
if _:
+1 -1
View File
@@ -7,7 +7,6 @@ See the file 'LICENSE' for copying permission
import re
from extra.safe2bin.safe2bin import safechardecode
from lib.core.agent import agent
from lib.core.bigarray import BigArray
from lib.core.common import Backend
@@ -33,6 +32,7 @@ from lib.core.settings import MAX_INT
from lib.core.settings import NULL
from lib.core.unescaper import unescaper
from lib.request import inject
from lib.utils.safe2bin import safechardecode
from thirdparty.six import unichr as _unichr
def pivotDumpTable(table, colList, count=None, blind=True, alias=None):
+2 -1
View File
@@ -15,6 +15,7 @@ import string
from lib.core.common import getSafeExString
from lib.core.common import openFile
from lib.core.compat import xrange
from lib.core.convert import getUnicode
from lib.core.data import logger
from thirdparty.six import unichr as _unichr
@@ -84,4 +85,4 @@ def purge(directory):
try:
shutil.rmtree(directory)
except OSError as ex:
logger.error("problem occurred while removing directory '%s' ('%s')" % (directory, getSafeExString(ex)))
logger.error("problem occurred while removing directory '%s' ('%s')" % (getUnicode(directory), getSafeExString(ex)))
@@ -1,23 +1,15 @@
#!/usr/bin/env python
"""
safe2bin.py - Simple safe(hex) to binary format converter
Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""
from __future__ import print_function
import binascii
import re
import string
import os
import sys
from optparse import OptionError
from optparse import OptionParser
if sys.version_info >= (3, 0):
xrange = range
text_type = str
@@ -49,10 +41,10 @@ def safecharencode(value):
"""
Returns safe representation of a given basestring value
>>> safecharencode(u'test123')
u'test123'
>>> safecharencode(u'test\x01\x02\xff')
u'test\\01\\02\\03\\ff'
>>> safecharencode(u'test123') == u'test123'
True
>>> safecharencode(u'test\x01\x02\xaf') == u'test\\\\x01\\\\x02\\xaf'
True
"""
retVal = value
@@ -107,37 +99,3 @@ def safechardecode(value, binary=False):
retVal[i] = safechardecode(value[i])
return retVal
def main():
usage = '%s -i <input file> [-o <output file>]' % sys.argv[0]
parser = OptionParser(usage=usage, version='0.1')
try:
parser.add_option('-i', dest='inputFile', help='Input file')
parser.add_option('-o', dest='outputFile', help='Output file')
(args, _) = parser.parse_args()
if not args.inputFile:
parser.error('Missing the input file, -h for help')
except (OptionError, TypeError) as ex:
parser.error(ex)
if not os.path.isfile(args.inputFile):
print('ERROR: the provided input file \'%s\' is not a regular file' % args.inputFile)
sys.exit(1)
f = open(args.inputFile, 'r')
data = f.read()
f.close()
if not args.outputFile:
args.outputFile = args.inputFile + '.bin'
f = open(args.outputFile, 'wb')
f.write(safechardecode(data))
f.close()
if __name__ == '__main__':
main()
+2 -1
View File
@@ -76,7 +76,8 @@ class SQLAlchemy(GenericConnector):
pass
elif "invalid literal for int() with base 10: '0b" in traceback.format_exc():
raise SqlmapConnectionException("SQLAlchemy connection issue ('https://bitbucket.org/zzzeek/sqlalchemy/issues/3975')")
raise
else:
pass
except SqlmapFilePathException:
raise
except Exception as ex:
+4 -2
View File
@@ -10,6 +10,7 @@ import re
from lib.core.common import Backend
from lib.core.common import Format
from lib.core.common import randomRange
from lib.core.common import randomStr
from lib.core.compat import xrange
from lib.core.convert import getUnicode
from lib.core.data import conf
@@ -74,13 +75,14 @@ class Fingerprint(GenericFingerprint):
("1.5", ("NULLIF(%d,%d) IS NULL", "EXISTS(SELECT CURRENT_TRANSACTION FROM RDB$DATABASE)")),
("2.0", ("EXISTS(SELECT CURRENT_TIME(0) FROM RDB$DATABASE)", "BIT_LENGTH(%d)>0", "CHAR_LENGTH(%d)>0")),
("2.1", ("BIN_XOR(%d,%d)=0", "PI()>0.%d", "RAND()<1.%d", "FLOOR(1.%d)>=0")),
# TODO: add test for Firebird 2.5
("2.5", ("'%s' SIMILAR TO '%s'",)), # Reference: https://firebirdsql.org/refdocs/langrefupd25-similar-to.html
("3.0", ("FALSE IS FALSE",)), # https://www.firebirdsql.org/file/community/conference-2014/pdf/02_fb.2014.whatsnew.30.en.pdf
)
for i in xrange(len(table)):
version, checks = table[i]
failed = False
check = checks[randomRange(0, len(checks) - 1)].replace("%d", getUnicode(randomRange(1, 100)))
check = checks[randomRange(0, len(checks) - 1)].replace("%d", getUnicode(randomRange(1, 100))).replace("%s", getUnicode(randomStr()))
result = inject.checkBooleanExpression(check)
if result:
+1 -1
View File
@@ -96,7 +96,7 @@ class Fingerprint(GenericFingerprint):
infoMsg = "actively fingerprinting %s" % DBMS.INFORMIX
logger.info(infoMsg)
for version in ("12.1", "11.7", "11.5"):
for version in ("14.1", "12.1", "11.7", "11.5", "10.0"):
output = inject.checkBooleanExpression("EXISTS(SELECT 1 FROM SYSMASTER:SYSDUAL WHERE DBINFO('VERSION,'FULL') LIKE '%%%s%%')" % version)
if output:
+2 -1
View File
@@ -94,7 +94,8 @@ class Fingerprint(GenericFingerprint):
("2008", "SYSDATETIME()=SYSDATETIME()"),
("2012", "CONCAT(NULL,NULL)=CONCAT(NULL,NULL)"),
("2014", "CHARINDEX('12.0.2000',@@version)>0"),
("2016", "ISJSON(NULL) IS NULL")
("2016", "ISJSON(NULL) IS NULL"),
("2017", "TRIM(NULL) IS NULL")
):
result = inject.checkBooleanExpression(check)
+1 -1
View File
@@ -53,7 +53,7 @@ class Fingerprint(GenericFingerprint):
(50600, 50646), # MySQL 5.6
(50700, 50726), # MySQL 5.7
(60000, 60014), # MySQL 6.0
(80000, 80015), # MySQL 8.0
(80000, 80017), # MySQL 8.0
)
index = -1
+4 -3
View File
@@ -21,6 +21,7 @@ from lib.core.enums import DBMS
from lib.core.enums import EXPECTED
from lib.core.enums import PAYLOAD
from lib.core.exception import SqlmapNoneDataException
from lib.core.settings import CURRENT_USER
from lib.request import inject
from plugins.generic.enumeration import Enumeration as GenericEnumeration
@@ -30,7 +31,7 @@ class Enumeration(GenericEnumeration):
rootQuery = queries[DBMS.ORACLE].roles
if conf.user == "CU":
if conf.user == CURRENT_USER:
infoMsg += " for current user"
conf.user = self.getCurrentUser()
@@ -55,7 +56,7 @@ class Enumeration(GenericEnumeration):
values = inject.getValue(query, blind=False, time=False)
if not values and not query2:
infoMsg = "trying with table USER_ROLE_PRIVS"
infoMsg = "trying with table 'USER_ROLE_PRIVS'"
logger.info(infoMsg)
return self.getRoles(query2=True)
@@ -116,7 +117,7 @@ class Enumeration(GenericEnumeration):
if not isNumPosStrValue(count):
if count != 0 and not query2:
infoMsg = "trying with table USER_SYS_PRIVS"
infoMsg = "trying with table 'USER_SYS_PRIVS'"
logger.info(infoMsg)
return self.getPrivileges(query2=True)
+1 -1
View File
@@ -105,7 +105,7 @@ class Fingerprint(GenericFingerprint):
logger.info(infoMsg)
# Reference: https://en.wikipedia.org/wiki/Oracle_Database
for version in ("18c", "12c", "11g", "10g", "9i", "8i"):
for version in ("18c", "12c", "11g", "10g", "9i", "8i", "7"):
number = int(re.search(r"([\d]+)", version).group(1))
output = inject.checkBooleanExpression("%d=(SELECT SUBSTR((VERSION),1,%d) FROM SYS.PRODUCT_COMPONENT_VERSION WHERE ROWNUM=1)" % (number, 1 if number < 10 else 2))
+2 -1
View File
@@ -10,6 +10,7 @@ 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 isListLike
from lib.core.common import isStackingAvailable
from lib.core.common import randomStr
@@ -104,7 +105,7 @@ class Takeover(GenericTakeover):
output = inject.getValue(query, resumeValue=False)
if isListLike(output):
output = os.linesep.join(output)
output = os.linesep.join(flattenValue(output))
self._cleanupCmd = "DROP TABLE %s" % self.cmdTblName
inject.goStacked(self._cleanupCmd)
+4 -3
View File
@@ -25,6 +25,7 @@ from lib.core.common import singleTimeLogMessage
from lib.core.common import singleTimeWarnMessage
from lib.core.common import unArrayizeValue
from lib.core.common import unsafeSQLIdentificatorNaming
from lib.core.convert import getConsoleLength
from lib.core.convert import getUnicode
from lib.core.data import conf
from lib.core.data import kb
@@ -269,7 +270,7 @@ class Entries(object):
else:
colEntry = unArrayizeValue(entry[index]) if index < len(entry) else u''
maxLen = max(len(column), len(DUMP_REPLACEMENTS.get(getUnicode(colEntry), getUnicode(colEntry))))
maxLen = max(getConsoleLength(column), getConsoleLength(DUMP_REPLACEMENTS.get(getUnicode(colEntry), getUnicode(colEntry))))
if maxLen > kb.data.dumpedTable[column]["length"]:
kb.data.dumpedTable[column]["length"] = maxLen
@@ -506,7 +507,7 @@ class Entries(object):
logger.info(infoMsg)
def dumpFoundColumn(self, dbs, foundCols, colConsider):
message = "do you want to dump entries? [Y/n] "
message = "do you want to dump found column(s) entries? [Y/n] "
if not readInput(message, default='Y', boolean=True):
return
@@ -573,7 +574,7 @@ class Entries(object):
conf.dumper.dbTableValues(data)
def dumpFoundTables(self, tables):
message = "do you want to dump tables' entries? [Y/n] "
message = "do you want to dump found table(s) entries? [Y/n] "
if not readInput(message, default='Y', boolean=True):
return
+1 -2
View File
@@ -33,7 +33,6 @@ from lib.core.enums import DBMS
from lib.core.enums import EXPECTED
from lib.core.enums import PAYLOAD
from lib.core.exception import SqlmapUndefinedMethod
from lib.core.settings import TAKEOVER_TABLE_PREFIX
from lib.core.settings import UNICODE_ENCODING
from lib.request import inject
@@ -43,7 +42,7 @@ class Filesystem(object):
"""
def __init__(self):
self.fileTblName = "%sfile" % TAKEOVER_TABLE_PREFIX
self.fileTblName = "%sfile" % conf.tablePrefix
self.tblField = "data"
def _checkFileLength(self, localFile, remoteFile, fileRead=False):
+1 -1
View File
@@ -137,7 +137,7 @@ class Miscellaneous(object):
self.delRemoteFile(self.webStagerFilePath)
self.delRemoteFile(self.webBackdoorFilePath)
if not isStackingAvailable() and not conf.direct:
if (not isStackingAvailable() or kb.udfFail) and not conf.direct:
return
if any((conf.osCmd, conf.osShell)) and Backend.isDbms(DBMS.PGSQL) and kb.copyExecTest:
+19 -5
View File
@@ -9,11 +9,13 @@ import os
from lib.core.common import Backend
from lib.core.common import getSafeExString
from lib.core.common import isDigit
from lib.core.common import isStackingAvailable
from lib.core.common import openFile
from lib.core.common import readInput
from lib.core.common import runningAsAdmin
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 OS
@@ -25,7 +27,6 @@ from lib.core.exception import SqlmapNotVulnerableException
from lib.core.exception import SqlmapSystemException
from lib.core.exception import SqlmapUndefinedMethod
from lib.core.exception import SqlmapUnsupportedDBMSException
from lib.core.settings import TAKEOVER_TABLE_PREFIX
from lib.takeover.abstraction import Abstraction
from lib.takeover.icmpsh import ICMPsh
from lib.takeover.metasploit import Metasploit
@@ -37,7 +38,7 @@ class Takeover(Abstraction, Metasploit, ICMPsh, Registry):
"""
def __init__(self):
self.cmdTblName = ("%soutput" % TAKEOVER_TABLE_PREFIX)
self.cmdTblName = ("%soutput" % conf.tablePrefix)
self.tblField = "data"
Abstraction.__init__(self)
@@ -79,7 +80,20 @@ class Takeover(Abstraction, Metasploit, ICMPsh, Registry):
raise SqlmapNotVulnerableException(errMsg)
self.getRemoteTempPath()
self.initEnv(web=web)
try:
self.initEnv(web=web)
except SqlmapFilePathException:
if not web:
infoMsg = "falling back to web backdoor method..."
logger.info(infoMsg)
web = True
kb.udfFail = True
self.initEnv(web=web)
else:
raise
if not web or (web and self.webBackdoorUrl is not None):
self.shell()
@@ -102,7 +116,7 @@ class Takeover(Abstraction, Metasploit, ICMPsh, Registry):
while True:
tunnel = readInput(msg, default='1')
if tunnel.isdigit() and int(tunnel) in (1, 2):
if isDigit(tunnel) and int(tunnel) in (1, 2):
tunnel = int(tunnel)
break
@@ -173,7 +187,7 @@ class Takeover(Abstraction, Metasploit, ICMPsh, Registry):
while True:
choice = readInput(msg, default='1')
if choice.isdigit() and int(choice) in (1, 2):
if isDigit(choice) and int(choice) in (1, 2):
choice = int(choice)
break
+9 -7
View File
@@ -39,6 +39,7 @@ from lib.core.enums import EXPECTED
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.threads import getCurrentThreadData
from lib.request import inject
from lib.utils.hash import attackCachedUsersPasswords
@@ -153,7 +154,7 @@ class Users(object):
rootQuery = queries[Backend.getIdentifiedDbms()].passwords
if conf.user == "CU":
if conf.user == CURRENT_USER:
infoMsg += " for current user"
conf.user = self.getCurrentUser()
@@ -362,7 +363,7 @@ class Users(object):
rootQuery = queries[Backend.getIdentifiedDbms()].privileges
if conf.user == "CU":
if conf.user == CURRENT_USER:
infoMsg += " for current user"
conf.user = self.getCurrentUser()
@@ -410,7 +411,7 @@ class Users(object):
values = inject.getValue(query, blind=False, time=False)
if not values and Backend.isDbms(DBMS.ORACLE) and not query2:
infoMsg = "trying with table USER_SYS_PRIVS"
infoMsg = "trying with table 'USER_SYS_PRIVS'"
logger.info(infoMsg)
return self.getPrivileges(query2=True)
@@ -446,7 +447,7 @@ class Users(object):
# In MySQL < 5.0 we get Y if the privilege is
# True, N otherwise
elif Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema:
if privilege.upper() == "Y":
if privilege.upper() == 'Y':
privileges.add(MYSQL_PRIVS[count])
# In Firebird we get one letter for each privilege
@@ -465,7 +466,7 @@ class Users(object):
i = 1
for priv in privs:
if priv.upper() in ("Y", "G"):
if priv.upper() in ('Y', 'G'):
for position, db2Priv in DB2_PRIVS.items():
if position == i:
privilege += ", " + db2Priv
@@ -525,7 +526,7 @@ class Users(object):
if not isNumPosStrValue(count):
if not retrievedUsers and Backend.isDbms(DBMS.ORACLE) and not query2:
infoMsg = "trying with table USER_SYS_PRIVS"
infoMsg = "trying with table 'USER_SYS_PRIVS'"
logger.info(infoMsg)
return self.getPrivileges(query2=True)
@@ -599,7 +600,8 @@ class Users(object):
# In Firebird we get one letter for each privilege
elif Backend.isDbms(DBMS.FIREBIRD):
privileges.add(FIREBIRD_PRIVS[privilege.strip()])
if privilege.strip() in FIREBIRD_PRIVS:
privileges.add(FIREBIRD_PRIVS[privilege.strip()])
# In Informix we get one letter for the highest privilege
elif Backend.isDbms(DBMS.INFORMIX):
+44 -30
View File
@@ -67,6 +67,10 @@ dropSetCookie = False
# sqlmap will also test for SQL injection on the HTTP User-Agent value.
agent =
# Imitate smartphone through HTTP User-Agent header.
# Valid: True or False
mobile = False
# Use randomly selected HTTP User-Agent header value.
# Valid: True or False
randomAgent = False
@@ -176,16 +180,19 @@ safeReqFile =
# Default: 0
safeFreq = 0
# Skip URL encoding of payload data
# Skip URL encoding of payload data.
# Valid: True or False
skipUrlEncode = False
# Parameter used to hold anti-CSRF token
# Parameter used to hold anti-CSRF token.
csrfToken =
# URL address to visit to extract anti-CSRF token
csrfUrl =
# HTTP method to use during anti-CSRF token page visit.
csrfMethod =
# Force usage of SSL/HTTPS
# Valid: True or False
forceSSL = False
@@ -343,6 +350,10 @@ regexp =
# code)
# code =
# Conduct thorough tests only if positive heuristic(s).
# Valid: True or False
smart = False
# Compare pages based only on the textual content.
# Valid: True or False
textOnly = False
@@ -683,6 +694,9 @@ sessionFile =
# Log all HTTP traffic into a textual file.
trafficFile =
# Set predefined answers (e.g. "quit=N,follow=N").
answers =
# Never ask for user input, use the default behaviour.
# Valid: True or False
batch = False
@@ -693,6 +707,10 @@ binaryFields =
# Check Internet connection before assessing the target.
checkInternet = False
# Clean up the DBMS from sqlmap specific UDF and tables.
# Valid: True or False
cleanup = False
# Crawl the website starting from the target URL.
# Valid: integer
# Default: 0
@@ -729,6 +747,11 @@ forms = False
# Valid: True or False
freshQueries = False
# Use Google dork results from specified page number.
# Valid: integer
# Default: 1
googlePage = 1
# Use hex conversion during data retrieval.
# Valid: True or False
hexConvert = False
@@ -752,15 +775,22 @@ repair = False
# Example: (google|yahoo)
scope =
# Skip heuristic detection of WAF/IPS protection.
# Valid: True or False
skipWaf = False
# Prefix used for temporary tables.
# Default: sqlmap
tablePrefix = sqlmap
# Select tests by payloads and/or titles (e.g. ROW)
testFilter =
# Skip tests by payloads and/or titles (e.g. BENCHMARK)
testSkip =
# Update sqlmap.
# Valid: True or False
updateAll = False
# Web server document root directory (e.g. "/var/www").
webRoot =
[Miscellaneous]
@@ -768,9 +798,6 @@ updateAll = False
# Run host OS command(s) when SQL injection is found.
alert =
# Set predefined answers (e.g. "quit=N,follow=N").
answers =
# Beep on question and/or when SQL injection is found.
# Valid: True or False
beep = False
@@ -779,10 +806,6 @@ beep = False
# Valid: True or False
checkPayload = False
# Clean up the DBMS from sqlmap specific UDF and tables.
# Valid: True or False
cleanup = False
# Check for missing (optional) sqlmap dependencies.
# Valid: True or False
dependencies = False
@@ -791,36 +814,27 @@ dependencies = False
# Valid: True or False
disableColoring = False
# Use Google dork results from specified page number.
# Valid: integer
# Default: 1
googlePage = 1
# Display list of available tamper scripts
# Valid: True or False
listTampers = False
# Imitate smartphone through HTTP User-Agent header.
# Valid: True or False
mobile = False
# Work in offline mode (only use session data)
# Valid: True or False
offline = False
# Skip heuristic detection of WAF/IPS protection.
# Valid: True or False
skipWaf = False
# Conduct thorough tests only if positive heuristic(s).
# Valid: True or False
smart = False
# Location of CSV results file in multiple targets mode.
resultsFile =
# Local directory for storing temporary files.
tmpDir =
# Web server document root directory (e.g. "/var/www").
webRoot =
# Adjust options for unstable connections.
# Valid: True or False
unstable = False
# Update sqlmap.
# Valid: True or False
updateAll = False
# Simple wizard interface for beginner users.
# Valid: True or False
+5
View File
@@ -263,6 +263,11 @@ def main():
logger.critical(errMsg)
raise SystemExit
elif all(_ in excMsg for _ in ("Access is denied", "subprocess", "metasploit")):
errMsg = "permission error occurred while running Metasploit"
logger.critical(errMsg)
raise SystemExit
elif all(_ in excMsg for _ in ("No such file", "_'")):
errMsg = "corrupted installation detected ('%s'). " % excMsg.strip().split('\n')[-1]
errMsg += "You should retrieve the latest development version from official GitHub "
+14 -5
View File
@@ -16,20 +16,29 @@ def dependencies():
pass
def randomIP():
numbers = []
octets = []
while not numbers or numbers[0] in (10, 172, 192):
numbers = random.sample(xrange(1, 255), 4)
while not octets or octets[0] in (10, 172, 192):
octets = random.sample(xrange(1, 255), 4)
return '.'.join(str(_) for _ in numbers)
return '.'.join(str(_) for _ in octets)
def tamper(payload, **kwargs):
"""
Append a fake HTTP header 'X-Forwarded-For'
Append a fake HTTP header 'X-Forwarded-For' (and alike)
"""
headers = kwargs.get("headers", {})
headers["X-Forwarded-For"] = randomIP()
headers["X-Client-Ip"] = randomIP()
headers["X-Real-Ip"] = randomIP()
headers["CF-Connecting-IP"] = randomIP()
headers["True-Client-IP"] = randomIP()
# Reference: https://developer.chrome.com/multidevice/data-compression-for-isps#proxy-connection
headers["Via"] = "1.1 Chrome-Compression-Proxy"
# Reference: https://wordpress.org/support/topic/blocked-country-gaining-access-via-cloudflare/#post-9812007
headers["CF-IPCountry"] = random.sample(('GB', 'US', 'FR', 'AU', 'CA', 'NZ', 'BE', 'DK', 'FI', 'IE', 'AT', 'IT', 'LU', 'NL', 'NO', 'PT', 'SE', 'ES', 'CH'), 1)[0]
return payload
+1 -1
View File
@@ -894,7 +894,7 @@ class Tag(PageElement):
def recursiveChildGenerator(self):
if not len(self.contents):
raise StopIteration
return # Note: https://stackoverflow.com/a/30217723 (PEP 479)
stopNode = self._lastRecursiveChild().next
current = self.contents[0]
while current is not stopNode:
+4 -2
View File
@@ -227,6 +227,9 @@ def unescape(data, entities, encoding=DEFAULT_ENCODING):
if data is None or "&" not in data:
return data
if isinstance(data, six.string_types):
encoding = None
def replace_entities(match, entities=entities, encoding=encoding):
ent = match.group()
if ent[1] == "#":
@@ -234,7 +237,7 @@ def unescape(data, entities, encoding=DEFAULT_ENCODING):
repl = entities.get(ent)
if repl is not None:
if type(repl) != type(""):
if type(repl) != type("") and encoding is not None:
try:
repl = repl.encode(encoding)
except UnicodeError:
@@ -279,7 +282,6 @@ def get_entitydefs():
entitydefs["&%s;" % name] = _unichr(codepoint)
return entitydefs
def issequence(x):
try:
x[0]
+2
View File
@@ -184,6 +184,8 @@ class AnsiToWin32(object):
if not (err.errno == 0 and retry > 0):
raise
self._write(text, retry-1)
except UnicodeError:
self.wrapped.write('?')
def convert_ansi(self, paramstring, command):
if self.convert:
+9
View File
@@ -0,0 +1,9 @@
#!/usr/bin/env python
#
# Copyright (c) 2019 Miroslav Stampar (@stamparm), MIT
# See the file 'LICENSE' for copying permission
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
pass
+13 -2
View File
@@ -161,10 +161,11 @@
"barracuda": {
"company": "Barracuda Networks",
"name": "Barracuda",
"regex": "\\bbarracuda_|barra_counter_session=|when this page occurred and the event ID found at the bottom of the page",
"regex": "\\bbarracuda_|barra_counter_session=|when this page occurred and the event ID found at the bottom of the page|<!--(0123456789){15}",
"signatures": [
"2676:RVdXum61OElCWapAYKYPk4JzWOtohM4JiUcMr2RWg1qQJbX3uhdOn9htOj+hXrAB16FcPxJPdLsXo2tKaK99n+i6c4VmkwI3FZjxtDtAeq6c36A4chS1XaTC",
"db27:RVdXum61OElCWapAYKYPk4JzWOtohM4JiUcMr2RWg1qQJbX3uhdOn9htOj+hXrAB16FcPxJPdLsXo2tKaK99n+i6c4RmkgI2FZjxtDtAeq6c3qA4chS1XaTC"
"db27:RVdXum61OElCWapAYKYPk4JzWOtohM4JiUcMr2RWg1qQJbX3uhdOn9htOj+hXrAB16FcPxJPdLsXo2tKaK99n+i6c4RmkgI2FZjxtDtAeq6c3qA4chS1XaTC",
"7e6b:RVdXum61OElCWapBYKYPk4JzWOtohM4JiUcMr2RXg1uQJbX3uhdOn9htOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4VmkwI3FZjxtDtAeq+c36A4chS1XaTC"
]
},
"bekchy": {
@@ -356,6 +357,16 @@
"regex": "The (ISA Server|server) denied the specified Uniform Resource Locator \\(URL\\)",
"signatures": []
},
"ithemes": {
"company": "iThemes",
"name": "iThemes Security",
"regex": "",
"signatures": [
"c70f:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMr2RWg1uQJLX3uhZOnthtOj+hXrAA16FcPhJOdLoXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC",
"71ee:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMr2RWg1qQJLX2uhZOnthtOj+hXrAA16FcPhJOdLoXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC"
],
"note": "Formerly Better WP Security"
},
"janusec": {
"company": "Janusec",
"name": "Janusec Application Gateway",
+5 -3
View File
@@ -27,7 +27,9 @@ import sys
import time
import zlib
if sys.version_info >= (3, 0):
PY3 = sys.version_info >= (3, 0)
if PY3:
import http.cookiejar
import http.client as httplib
import urllib.request
@@ -58,7 +60,7 @@ else:
HTTPCookieProcessor = urllib2.HTTPCookieProcessor
NAME = "identYwaf"
VERSION = "1.0.114"
VERSION = "1.0.122"
BANNER = r"""
` __ __ `
____ ___ ___ ____ ______ `| T T` __ __ ____ _____
@@ -356,7 +358,7 @@ def init():
os.chdir(os.path.abspath(os.path.dirname(__file__)))
# Reference: http://blog.mathieu-leplatre.info/python-utf-8-print-fails-when-redirecting-stdout.html
if IS_TTY:
if not PY3 and not IS_TTY:
sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout)
print(colorize("[o] initializing handlers..."))