Compare commits

..
121 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
54 changed files with 1456 additions and 326 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
+1 -1
View File
@@ -4,7 +4,7 @@
sqlmap is an open source penetration testing tool that automates the process of detecting and exploiting SQL injection flaws and taking over of database servers. It comes with a powerful detection engine, many niche features for the ultimate penetration tester, and a broad range of switches including database fingerprinting, over data fetching from the database, accessing the underlying file system, and executing commands on the operating system via out-of-band connections.
**The sqlmap project is 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
----
+143
View File
@@ -203,6 +203,15 @@
/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
@@ -212,6 +221,13 @@
/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
@@ -1659,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
+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"/>
+53 -15
View File
@@ -16,8 +16,10 @@ 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
@@ -29,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
@@ -96,15 +97,27 @@ 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"):
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] and isinstance(params[key], (tuple, list)):
params[key] = params[key][-1]
@@ -112,35 +125,54 @@ class ReqHandler(BaseHTTPRequestHandler):
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")
@@ -149,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()
+19 -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
@@ -153,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 []
@@ -163,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 ""
@@ -599,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
@@ -786,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
@@ -925,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
@@ -1099,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)
@@ -1118,6 +1128,7 @@ def heuristicCheckSqlInjection(place, parameter):
logger.info(infoMsg)
break
kb.disableHtmlDecoding = False
kb.heuristicMode = False
return kb.heuristicTest
@@ -1512,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"
+6 -9
View File
@@ -36,6 +36,7 @@ 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
@@ -203,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
@@ -257,10 +259,6 @@ 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.resultsFile, getSafeExString(ex))
@@ -293,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
@@ -301,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)
@@ -317,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)
+11 -4
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:
@@ -451,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)
@@ -1115,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)
@@ -1124,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
+76 -21
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):
"""
@@ -1696,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
@@ -1969,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
@@ -2762,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
@@ -3483,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
@@ -3562,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()
@@ -3593,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)
@@ -3806,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)
@@ -4020,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
@@ -4034,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()
@@ -4059,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()
@@ -4318,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
@@ -4419,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:
@@ -4474,9 +4525,13 @@ def findPageForms(content, url, raise_=False, addToTargets=False):
# 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
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:
@@ -4502,7 +4557,7 @@ def findPageForms(content, url, raise_=False, addToTargets=False):
data = data.strip("['\"]")
retVal.add((url, HTTPMETHOD.POST, data, conf.cookie, None))
if not retVal:
if not retVal and not conf.crawlDepth:
errMsg = "there were no forms found at the given target URL"
if raise_:
raise SqlmapGenericException(errMsg)
@@ -4869,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))
+11 -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:
@@ -391,4 +401,4 @@ def getConsoleLength(value):
else:
retVal = len(value)
return retVal
return retVal
+3 -4
View File
@@ -31,6 +31,7 @@ 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
@@ -137,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:
@@ -559,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
@@ -631,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()
+51 -60
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
@@ -133,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
@@ -336,29 +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]
if not re.search(r"(?i)\Ahttp[s]*://", target):
target = "http://%s" % target
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():
"""
@@ -396,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):
@@ -409,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]"
@@ -443,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
@@ -471,15 +438,13 @@ def _findPageForms():
infoMsg = "searching for forms"
logger.info(infoMsg)
if not any((conf.bulkFile, conf.googleDork, conf.sitemapUrl)):
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()
@@ -1455,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():
"""
@@ -1557,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)
@@ -1653,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:
@@ -1770,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
@@ -1900,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
@@ -1942,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"):
@@ -1952,7 +1938,6 @@ def _setKnowledgeBaseAttributes(flushAll=True):
kb.mergeCookies = None
kb.multipleCtrlC = False
kb.negativeLogic = False
kb.normalizeCrawlingChoice = None
kb.nullConnection = None
kb.oldMsf = None
kb.orderByColumns = None
@@ -2006,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
@@ -2022,15 +2006,19 @@ def _setKnowledgeBaseAttributes(flushAll=True):
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()
@@ -2497,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:
@@ -2591,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)
@@ -2599,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)
@@ -2666,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()
@@ -2681,7 +2673,6 @@ def init():
_setSafeVisit()
_doSearch()
_setBulkMultipleTargets()
_setSitemapTargets()
_checkTor()
_setCrawler()
_findPageForms()
-1
View File
@@ -19,7 +19,6 @@ optDict = {
"sessionFile": "string",
"googleDork": "string",
"configFile": "string",
"sitemapUrl": "string",
},
"Request": {
+14 -4
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.11.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"
@@ -597,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
+3
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:
+65 -14
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,26 +102,48 @@ def vulnTest():
thread.daemon = True
thread.start()
for options, checks in (
("--flush-session", ("CloudFlare",)),
("--flush-session --data='{\"id\": 1}' --banner", ("Payload: {\"id\"", "banner: '3")),
("--flush-session --parse-errors --eval=\"id2=2\" --referer=\"localhost\" --cookie=\"PHPSESSID=d41d8cd98f00b204e9800998ecf8427e\"", (": syntax error", "Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", "back-end DBMS: SQLite", "3 columns")),
("--banner --schema --dump -T users --binary-fields=surname --where \"id>3\"", ("banner: '3", "INTEGER", "TEXT", "id", "name", "surname", "2 entries", "6E616D6569736E756C6C")),
("--all --tamper=between,randomcase", ("5 entries", "luther", "blisset", "fluffy", "179ad45c6ce2cb97cf1029e212046e81", "NULL", "nameisnull", "testpass")),
("-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:
+7 -1
View File
@@ -123,7 +123,7 @@ 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))
@@ -185,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:
+26 -18
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")
@@ -783,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)
@@ -850,7 +850,6 @@ def cmdLineParser(argv=None):
break
_ = []
prompt = False
advancedHelp = True
extraHeaders = []
tamperIndex = None
@@ -862,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 = ""
@@ -909,6 +913,7 @@ 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 re.search(r"\A(http|www\.|\w[\w.-]+\.\w{2,})", argv[i]) is not None:
@@ -922,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"):
@@ -994,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():
+43 -33
View File
@@ -48,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
@@ -260,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
@@ -333,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
@@ -427,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)
+32 -8
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):
@@ -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:
+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"
+1 -1
View File
@@ -110,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:
+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
+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
+2
View File
@@ -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)))
+66 -48
View File
@@ -41,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()
@@ -68,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
@@ -94,6 +98,7 @@ def crawl(target):
tags = soup('a')
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")
@@ -115,7 +120,7 @@ def crawl(target):
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
@@ -123,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
@@ -133,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
@@ -189,33 +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))
if kb.normalizeCrawlingChoice is None:
message = "do you want to normalize "
message += "crawling results [Y/n] "
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)
kb.normalizeCrawlingChoice = readInput(message, default='Y', boolean=True)
if kb.normalizeCrawlingChoice:
seen = set()
results = OrderedSet()
if kb.normalizeCrawlingChoice:
seen = set()
results = OrderedSet()
for target in kb.targets:
match = re.search(r"/[^/?]*\?.*\Z", target[0])
if match:
key = re.sub(r"=[^=&]*", "=", match.group(0))
if key not in seen:
results.add(target)
seen.add(key)
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
kb.targets = results
storeResultsToFile(kb.targets)
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")
+3
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:
+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)
+5 -5
View File
@@ -78,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
@@ -112,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
@@ -145,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)
@@ -491,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
@@ -562,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 = {}
+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)
+1 -1
View File
@@ -84,7 +84,7 @@ class Takeover(Abstraction, Metasploit, ICMPsh, Registry):
try:
self.initEnv(web=web)
except SqlmapFilePathException:
if not web:
if not web and not conf.direct:
infoMsg = "falling back to web backdoor method..."
logger.info(infoMsg)
+4 -8
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]
+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:
_ = ""