Compare commits

..

17 Commits
1.5.6 ... 1.5.8

Author SHA1 Message Date
Miroslav Stampar
06cd97f097 Trivial update 2021-08-02 02:38:41 +02:00
Miroslav Stampar
293772348c Fixes #4751 2021-07-28 00:34:10 +02:00
Miroslav Stampar
2679c650aa Trivial update 2021-07-23 11:04:30 +02:00
Miroslav Stampar
179a6edf92 Implements swagger API specs (#4746) 2021-07-23 00:08:58 +02:00
Miroslav Stampar
8af87c7ea6 More text update 2021-07-19 13:58:54 +02:00
Miroslav Stampar
3a1dd163ec Minor text update (#4738) 2021-07-19 13:55:51 +02:00
Colinatorr
f8a9288953 fix: typo (#4740) 2021-07-19 13:50:23 +02:00
Miroslav Stampar
8895b7d09d Fixes #4736 2021-07-15 10:01:17 +02:00
Miroslav Stampar
fa05878712 Fixes #4733 2021-07-14 01:10:33 +02:00
Miroslav Stampar
795b9e6521 Fixes #4731 2021-07-12 00:35:50 +02:00
Miroslav Stampar
1f3a1410f2 Fixes #4727 2021-07-04 23:45:22 +02:00
Miroslav Stampar
69c679cf06 Fixes #4728 2021-07-04 23:07:55 +02:00
Miroslav Stampar
5ea08b397a Fixes #4710 2021-06-24 21:03:22 +02:00
Miroslav Stampar
7c41967865 Fixes #4719 2021-06-21 17:17:41 +02:00
Miroslav Stampar
255dce8c49 Fixes #4712 2021-06-15 21:04:51 +02:00
Miroslav Stampar
7c7c338705 Fixes #4705 2021-06-11 09:48:00 +02:00
Miroslav Stampar
63073a1873 15% speedup in some cases (avoiding heuristic char detection) 2021-06-08 21:48:43 +02:00
18 changed files with 280 additions and 25 deletions

View File

@@ -1209,7 +1209,7 @@
</users>
<passwords>
<inband query="SELECT USER_NAME,PASSWORD FROM SYSTEM_.SYS_USERS_" condition="USER_NAME"/>
<blind query="SELECT PASSWORD FROM SYSTEM_.SYS_USERS_ WHERE USER_NAME='%s'" count="SELECT COUNT(PASSWORD) FROM SYSTEM_.SYS_USERS_ WHERE USER_NAME='%s'"/>
<blind query="SELECT PASSWORD FROM SYSTEM_.SYS_USERS_ WHERE USER_NAME='%s' LIMIT %d,1" count="SELECT COUNT(PASSWORD) FROM SYSTEM_.SYS_USERS_ WHERE USER_NAME='%s'"/>
</passwords>
<privileges>
<inband query="SELECT USER_NAME,PRIV_NAME FROM SYSTEM_.SYS_GRANT_OBJECT_ JOIN SYSTEM_.SYS_PRIVILEGES_ ON SYSTEM_.SYS_GRANT_OBJECT_.PRIV_ID=SYSTEM_.SYS_PRIVILEGES_.PRIV_ID JOIN SYSTEM_.SYS_USERS_ ON SYSTEM_.SYS_USERS_.USER_ID=SYSTEM_.SYS_GRANT_OBJECT_.GRANTEE_ID" condition="USER_NAME"/>

View File

@@ -435,7 +435,7 @@ def checkSqlInjection(place, parameter, value):
origValue = origValue.split(kb.customInjectionMark)[0]
origValue = re.search(r"(\w*)\Z", origValue).group(1)
# Threat the parameter original value according to the
# Treat the parameter original value according to the
# test's <where> tag
if where == PAYLOAD.WHERE.ORIGINAL or conf.prefix:
if kb.tamperFunctions:

View File

@@ -26,6 +26,7 @@ from lib.core.common import openFile
from lib.core.common import prioritySortColumns
from lib.core.common import randomInt
from lib.core.common import safeCSValue
from lib.core.common import unArrayizeValue
from lib.core.common import unsafeSQLIdentificatorNaming
from lib.core.compat import xrange
from lib.core.convert import getBytes
@@ -116,6 +117,9 @@ class Dump(object):
if conf.api:
self._write(data, content_type=content_type)
if isListLike(data) and len(data) == 1:
data = unArrayizeValue(data)
if isListLike(data):
self.lister(header, data, content_type, sort)
elif data is not None:

View File

@@ -29,7 +29,7 @@ class Replication(object):
self.cursor = self.connection.cursor()
except sqlite3.OperationalError as ex:
errMsg = "error occurred while opening a replication "
errMsg += "file '%s' ('%s')" % (self.filepath, getSafeExString(ex))
errMsg += "file '%s' ('%s')" % (dbpath, getSafeExString(ex))
raise SqlmapConnectionException(errMsg)
class DataType(object):

View File

@@ -16,10 +16,11 @@ import time
from lib.core.enums import DBMS
from lib.core.enums import DBMS_DIRECTORY_NAME
from lib.core.enums import OS
from thirdparty import six
from thirdparty.six import unichr as _unichr
# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
VERSION = "1.5.6.0"
VERSION = "1.5.8.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)
@@ -906,6 +907,9 @@ KB_CHARS_BOUNDARY_CHAR = 'q'
# Letters of lower frequency used in kb.chars
KB_CHARS_LOW_FREQUENCY_ALPHABET = "zqxjkvbp"
# Printable bytes
PRINTABLE_BYTES = set(bytes(string.printable, "ascii") if six.PY3 else string.printable)
# SQL keywords used for splitting in HTTP chunked transfer encoded requests (switch --chunk)
HTTP_CHUNKED_SPLIT_KEYWORDS = ("SELECT", "UPDATE", "INSERT", "FROM", "LOAD_FILE", "UNION", "information_schema", "sysdatabases", "msysaccessobjects", "msysqueries", "sysmodules")

View File

@@ -166,6 +166,6 @@ def update():
infoMsg += "https://github.com/sqlmapproject/sqlmap/downloads"
else:
infoMsg = "for Linux platform it's recommended "
infoMsg += "to install a standard 'git' package (e.g.: 'sudo apt install git')"
infoMsg += "to install a standard 'git' package (e.g.: 'apt install git')"
logger.info(infoMsg)

View File

@@ -48,6 +48,7 @@ from lib.core.settings import IDENTYWAF_PARSE_LIMIT
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 PRINTABLE_BYTES
from lib.core.settings import SELECT_FROM_TABLE_REGEX
from lib.core.settings import UNICODE_ENCODING
from lib.core.settings import VIEWSTATE_REGEX
@@ -324,7 +325,7 @@ def decodePage(page, contentEncoding, contentType, percentDecode=True):
metaCharset = checkCharEncoding(extractRegexResult(META_CHARSET_REGEX, page))
if (any((httpCharset, metaCharset)) and not all((httpCharset, metaCharset))) or (httpCharset == metaCharset and all((httpCharset, metaCharset))):
if (any((httpCharset, metaCharset)) and (not all((httpCharset, metaCharset)) or isinstance(page, six.binary_type) and all(_ in PRINTABLE_BYTES for _ in page))) or (httpCharset == metaCharset and all((httpCharset, metaCharset))):
kb.pageEncoding = httpCharset or metaCharset # Reference: http://bytes.com/topic/html-css/answers/154758-http-equiv-vs-true-header-has-precedence
debugMsg = "declared web page charset '%s'" % kb.pageEncoding
singleTimeLogMessage(debugMsg, logging.DEBUG, debugMsg)

View File

@@ -626,7 +626,7 @@ class Connect(object):
if conn:
code = (code or conn.code) if conn.code == kb.originalCode else conn.code # do not override redirection code (for comparison purposes)
responseHeaders = conn.info()
responseHeaders[URI_HTTP_HEADER] = conn.geturl()
responseHeaders[URI_HTTP_HEADER] = conn.geturl() if hasattr(conn, "geturl") else url
if hasattr(conn, "redurl"):
responseHeaders[HTTP_HEADER.LOCATION] = conn.redurl
@@ -1275,7 +1275,7 @@ class Connect(object):
while True:
try:
compile(getBytes(conf.evalCode.replace(';', '\n')), "", "exec")
compile(getBytes(re.sub(r"\s*;\s*", "\n", conf.evalCode)), "", "exec")
except SyntaxError as ex:
if ex.text:
original = replacement = ex.text.strip()

View File

@@ -125,7 +125,7 @@ class SmartRedirectHandler(_urllib.request.HTTPRedirectHandler):
delimiter = conf.cookieDel or DEFAULT_COOKIE_DELIMITER
last = None
for part in req.headers.get(HTTP_HEADER.COOKIE, "").split(delimiter) + ([headers[HTTP_HEADER.SET_COOKIE]] if HTTP_HEADER.SET_COOKIE in headers else []):
for part in getUnicode(req.headers.get(HTTP_HEADER.COOKIE, "")).split(delimiter) + ([headers[HTTP_HEADER.SET_COOKIE]] if HTTP_HEADER.SET_COOKIE in headers else []):
if '=' in part:
part = part.strip()
key, value = part.split('=', 1)

View File

@@ -724,7 +724,7 @@ def server(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, adapter=REST
errMsg += "List of supported adapters: %s" % ', '.join(sorted(list(server_names.keys())))
else:
errMsg = "Server support for adapter '%s' is not installed on this system " % adapter
errMsg += "(Note: you can try to install it with 'sudo apt install python-%s' or 'sudo pip%s install %s')" % (adapter, '3' if six.PY3 else "", adapter)
errMsg += "(Note: you can try to install it with 'apt install python-%s' or 'pip%s install %s')" % (adapter, '3' if six.PY3 else "", adapter)
logger.critical(errMsg)
def _client(url, options=None):

View File

@@ -165,7 +165,7 @@ class Response(object):
try:
content = response.read()
except _http_client.IncompleteRead:
content = raw[raw.find("\r\n\r\n") + 4:].rstrip("\r\n")
content = raw[raw.find(b"\r\n\r\n") + 4:].rstrip(b"\r\n")
return cls(httpVersion="HTTP/1.1" if response.version == 11 else "HTTP/1.0",
status=response.status,

View File

@@ -10,7 +10,9 @@ import re
import string
import sys
if sys.version_info >= (3, 0):
PY3 = sys.version_info >= (3, 0)
if PY3:
xrange = range
text_type = str
string_types = (str,)
@@ -92,7 +94,7 @@ def safechardecode(value, binary=False):
if binary:
if isinstance(retVal, text_type):
retVal = retVal.encode("utf8")
retVal = retVal.encode("utf8", errors="surrogatepass" if PY3 else "strict")
elif isinstance(value, (list, tuple)):
for i in xrange(len(value)):

View File

@@ -35,6 +35,7 @@ from lib.core.exception import SqlmapConnectionException
from lib.core.exception import SqlmapFilePathException
from lib.core.exception import SqlmapMissingDependence
from plugins.generic.connector import Connector as GenericConnector
from thirdparty import six
def getSafeExString(ex, encoding=None): # Cross-referenced function
raise NotImplementedError
@@ -88,7 +89,7 @@ class SQLAlchemy(GenericConnector):
self.printConnected()
else:
raise SqlmapMissingDependence("SQLAlchemy not available")
raise SqlmapMissingDependence("SQLAlchemy not available (e.g. 'pip%s install SQLAlchemy')" % ('3' if six.PY3 else ""))
def fetchall(self):
try:

View File

@@ -45,9 +45,9 @@ class Fingerprint(GenericFingerprint):
# Reference: https://dev.mysql.com/doc/relnotes/mysql/<major>.<minor>/en/
versions = (
(80000, 80028), # MySQL 8.0
(80000, 80029), # MySQL 8.0
(60000, 60014), # MySQL 6.0
(50700, 50736), # MySQL 5.7
(50700, 50737), # MySQL 5.7
(50600, 50652), # MySQL 5.6
(50500, 50563), # MySQL 5.5
(50400, 50404), # MySQL 5.4

View File

@@ -618,7 +618,7 @@ class Databases(object):
query = rootQuery.inband.query % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db))
query += condQuery
if Backend.isFork(FORK.DRIZZLE):
if Backend.isDbms(DBMS.MYSQL) and Backend.isFork(FORK.DRIZZLE):
query = re.sub("column_type", "data_type", query, flags=re.I)
elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE, DBMS.MIMERSQL):
@@ -1022,7 +1022,7 @@ class Databases(object):
rootQuery = queries[Backend.getIdentifiedDbms()].statements
if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct:
if Backend.isFork(FORK.DRIZZLE):
if Backend.isDbms(DBMS.MYSQL) and Backend.isFork(FORK.DRIZZLE):
query = rootQuery.inband.query2
else:
query = rootQuery.inband.query
@@ -1049,7 +1049,7 @@ class Databases(object):
query = rootQuery.blind.count
if Backend.isFork(FORK.DRIZZLE):
if Backend.isDbms(DBMS.MYSQL) and Backend.isFork(FORK.DRIZZLE):
query = re.sub("INFORMATION_SCHEMA", "DATA_DICTIONARY", query, flags=re.I)
count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS)
@@ -1077,7 +1077,7 @@ class Databases(object):
if isNoneValue(value):
query = rootQuery.blind.query % index
if Backend.isFork(FORK.DRIZZLE):
if Backend.isDbms(DBMS.MYSQL) and Backend.isFork(FORK.DRIZZLE):
query = re.sub("INFORMATION_SCHEMA", "DATA_DICTIONARY", query, flags=re.I)
value = unArrayizeValue(inject.getValue(query, union=False, error=False))

View File

@@ -158,7 +158,7 @@ class Miscellaneous(object):
udfDict = {"master..new_xp_cmdshell": {}}
if udfDict is None:
udfDict = self.sysUdfs
udfDict = getattr(self, "sysUdfs", {})
for udf, inpRet in udfDict.items():
message = "do you want to remove UDF '%s'? [Y/n] " % udf

View File

@@ -81,7 +81,7 @@ class Users(object):
if Backend.isDbms(DBMS.MYSQL):
self.getCurrentUser()
if Backend.isFork(FORK.DRIZZLE):
if Backend.isDbms(DBMS.MYSQL) and Backend.isFork(FORK.DRIZZLE):
kb.data.isDba = "root" in (kb.data.currentUser or "")
elif kb.data.currentUser:
query = queries[Backend.getIdentifiedDbms()].is_dba.query % kb.data.currentUser.split("@")[0]
@@ -106,7 +106,7 @@ class Users(object):
condition |= (Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema)
if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct:
if Backend.isFork(FORK.DRIZZLE):
if Backend.isDbms(DBMS.MYSQL) and Backend.isFork(FORK.DRIZZLE):
query = rootQuery.inband.query3
elif condition:
query = rootQuery.inband.query2
@@ -126,7 +126,7 @@ class Users(object):
infoMsg = "fetching number of database users"
logger.info(infoMsg)
if Backend.isFork(FORK.DRIZZLE):
if Backend.isDbms(DBMS.MYSQL) and Backend.isFork(FORK.DRIZZLE):
query = rootQuery.blind.count3
elif condition:
query = rootQuery.blind.count2
@@ -147,7 +147,7 @@ class Users(object):
for index in indexRange:
if Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MAXDB):
query = rootQuery.blind.query % (kb.data.cachedUsers[-1] if kb.data.cachedUsers else " ")
elif Backend.isFork(FORK.DRIZZLE):
elif Backend.isDbms(DBMS.MYSQL) and Backend.isFork(FORK.DRIZZLE):
query = rootQuery.blind.query3 % index
elif condition:
query = rootQuery.blind.query2 % index

243
sqlmapapi.yaml Normal file
View File

@@ -0,0 +1,243 @@
openapi: 3.0.1
info:
title: sqlmapapi OpenAPI/Swagger specification
version: '0.1'
paths:
/version:
get:
description: Fetch server version
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
version:
type: string
example: "1.5.7.7#dev"
success:
type: boolean
example: true
/task/new:
get:
description: Create a new task
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
taskid:
type: string
example: "fad44d6beef72285"
success:
type: boolean
example: true
/scan/{taskid}/start:
post:
description: Launch a scan
parameters:
- in: path
name: taskid
required: true
schema:
type: string
description: Scan task ID
requestBody:
content:
application/json:
schema:
type: object
properties:
url:
type: string
examples:
'0':
value: '{"url":"http://testphp.vulnweb.com/artists.php?artist=1"}'
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
engineid:
type: integer
example: 19720
success:
type: boolean
example: true
/scan/{taskid}/stop:
get:
description: Stop a scan
parameters:
- in: path
name: taskid
required: true
schema:
type: string
description: Scan task ID
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
example: true
/scan/{taskid}/status:
get:
description: Fetch status of a scan
parameters:
- in: path
name: taskid
required: true
schema:
type: string
description: Scan task ID
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
status:
type: string
example: terminated
returncode:
type: integer
example: 0
success:
type: boolean
example: true
/scan/{taskid}/list:
get:
description: List options for a given task ID
parameters:
- in: path
name: taskid
required: true
schema:
type: string
description: Scan task ID
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
example: true
options:
type: array
items:
type: object
/scan/{taskid}/data:
get:
description: Retrieve the scan resulting data
parameters:
- in: path
name: taskid
required: true
schema:
type: string
description: Scan task ID
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
type: object
success:
type: boolean
example: true
error:
type: array
items:
type: object
/scan/{taskid}/log:
get:
description: Retrieve the log messages
parameters:
- in: path
name: taskid
required: true
schema:
type: string
description: Scan task ID
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
log:
type: array
items:
type: object
success:
type: boolean
example: true
/scan/{taskid}/kill:
get:
description: Kill a scan
parameters:
- in: path
name: taskid
required: true
schema:
type: string
description: Scan task ID
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
example: true
/task/{taskid}/delete:
get:
description: Delete an existing task
parameters:
- in: path
name: taskid
required: true
schema:
type: string
description: Scan task ID
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
example: true