diff --git a/lib/core/common.py b/lib/core/common.py index 6d83b31bb..3ce658418 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -3207,13 +3207,13 @@ def decodeIntToUnicode(value): if Backend.isDbms(DBMS.MYSQL): # https://github.com/sqlmapproject/sqlmap/issues/1531 - retVal = getUnicode(raw, conf.charset or UNICODE_ENCODING) + retVal = getUnicode(raw, conf.encoding or UNICODE_ENCODING) elif Backend.isDbms(DBMS.MSSQL): retVal = getUnicode(raw, "UTF-16-BE") elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.ORACLE): retVal = unichr(value) else: - retVal = getUnicode(raw, conf.charset) + retVal = getUnicode(raw, conf.encoding) else: retVal = getUnicode(chr(value)) except: diff --git a/lib/core/option.py b/lib/core/option.py index 9ed110bb4..366f8e082 100755 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1409,8 +1409,8 @@ def _setHTTPExtraHeaders(): raise SqlmapSyntaxException(errMsg) elif not conf.requestFile and len(conf.httpHeaders or []) < 2: - if conf.charset: - conf.httpHeaders.append((HTTP_HEADER.ACCEPT_CHARSET, "%s;q=0.7,*;q=0.1" % conf.charset)) + if conf.encoding: + conf.httpHeaders.append((HTTP_HEADER.ACCEPT_CHARSET, "%s;q=0.7,*;q=0.1" % conf.encoding)) # Invalidating any caching mechanism in between # Reference: http://stackoverflow.com/a/1383359 @@ -2577,15 +2577,15 @@ def _basicOptionValidation(): errMsg += "format : (e.g. \"root:pass\")" raise SqlmapSyntaxException(errMsg) - if conf.charset: - _ = checkCharEncoding(conf.charset, False) + if conf.encoding: + _ = checkCharEncoding(conf.encoding, False) if _ is None: - errMsg = "unknown charset '%s'. Please visit " % conf.charset + errMsg = "unknown charset '%s'. Please visit " % conf.encoding errMsg += "'%s' to get the full list of " % CODECS_LIST_PAGE errMsg += "supported charsets" raise SqlmapSyntaxException(errMsg) else: - conf.charset = _ + conf.encoding = _ if conf.loadCookies: if not os.path.exists(conf.loadCookies): diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 5dfaecb9f..15b0933fb 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -201,6 +201,7 @@ optDict = { "crawlExclude": "string", "csvDel": "string", "dumpFormat": "string", + "encoding": "string", "eta": "boolean", "flushSession": "boolean", "forms": "boolean", diff --git a/lib/core/settings.py b/lib/core/settings.py index 7ddf0f092..6d889427f 100755 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import DBMS_DIRECTORY_NAME from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.1.9.22" +VERSION = "1.1.9.23" 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) @@ -67,6 +67,7 @@ BOUNDED_INJECTION_MARKER = "__BOUNDED_INJECTION_MARK__" RANDOM_INTEGER_MARKER = "[RANDINT]" RANDOM_STRING_MARKER = "[RANDSTR]" SLEEP_TIME_MARKER = "[SLEEPTIME]" +INFERENCE_MARKER = "[INFERENCE]" PAYLOAD_DELIMITER = "__PAYLOAD_DELIMITER__" CHAR_INFERENCE_MARK = "%c" @@ -175,6 +176,9 @@ INFERENCE_UNKNOWN_CHAR = '?' # Character used for operation "greater" in inference INFERENCE_GREATER_CHAR = ">" +# Character used for operation "greater or equal" in inference +INFERENCE_GREATER_EQUALS_CHAR = ">=" + # Character used for operation "equals" in inference INFERENCE_EQUALS_CHAR = "=" diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 001a70f0f..d24f59def 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -631,9 +631,6 @@ def cmdLineParser(argv=None): general.add_option("--binary-fields", dest="binaryFields", help="Result fields having binary values (e.g. \"digest\")") - general.add_option("--charset", dest="charset", - help="Force character encoding used for data retrieval") - general.add_option("--check-internet", dest="checkInternet", action="store_true", help="Check Internet connection before assessing the target") @@ -648,9 +645,15 @@ def cmdLineParser(argv=None): help="Delimiting character used in CSV output " "(default \"%s\")" % defaults.csvDel) + general.add_option("--charset", dest="charset", + help="Blind SQL injection charset (e.g. \"0123456789abcdef\")") + general.add_option("--dump-format", dest="dumpFormat", help="Format of dumped data (CSV (default), HTML or SQLITE)") + general.add_option("--encoding", dest="encoding", + help="Character encoding used for data retrieval (e.g. GBK)") + general.add_option("--eta", dest="eta", action="store_true", help="Display for each output the estimated time of arrival") diff --git a/lib/request/basic.py b/lib/request/basic.py index cebac6f82..7ec83b76f 100644 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -279,7 +279,7 @@ def decodePage(page, contentEncoding, contentType): kb.pageCompress = False raise SqlmapCompressionException - if not conf.charset: + if not conf.encoding: httpCharset, metaCharset = None, None # Reference: http://stackoverflow.com/questions/1020892/python-urllib2-read-to-unicode @@ -296,7 +296,7 @@ def decodePage(page, contentEncoding, contentType): else: kb.pageEncoding = None else: - kb.pageEncoding = conf.charset + kb.pageEncoding = conf.encoding # can't do for all responses because we need to support binary files too if contentType and not isinstance(page, unicode) and "text/" in contentType.lower(): diff --git a/lib/takeover/web.py b/lib/takeover/web.py index 5dab222f4..77216a9e4 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -144,7 +144,7 @@ class Web: randInt = randomInt() query += "OR %d=%d " % (randInt, randInt) - query += getSQLSnippet(DBMS.MYSQL, "write_file_limit", OUTFILE=outFile, HEXSTRING=hexencode(uplQuery, conf.charset)) + query += getSQLSnippet(DBMS.MYSQL, "write_file_limit", OUTFILE=outFile, HEXSTRING=hexencode(uplQuery, conf.encoding)) query = agent.prefixQuery(query) query = agent.suffixQuery(query) payload = agent.payload(newValue=query) diff --git a/lib/takeover/xp_cmdshell.py b/lib/takeover/xp_cmdshell.py index dabad3b60..8f0e42c06 100644 --- a/lib/takeover/xp_cmdshell.py +++ b/lib/takeover/xp_cmdshell.py @@ -163,7 +163,7 @@ class XP_cmdshell: # Obfuscate the command to execute, also useful to bypass filters # on single-quotes self._randStr = randomStr(lowercase=True) - self._cmd = "0x%s" % hexencode(cmd, conf.charset) + self._cmd = "0x%s" % hexencode(cmd, conf.encoding) self._forgedCmd = "DECLARE @%s VARCHAR(8000);" % self._randStr self._forgedCmd += "SET @%s=%s;" % (self._randStr, self._cmd) diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index 100709480..10e9c06b9 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -39,7 +39,9 @@ from lib.core.settings import CHAR_INFERENCE_MARK from lib.core.settings import INFERENCE_BLANK_BREAK from lib.core.settings import INFERENCE_UNKNOWN_CHAR from lib.core.settings import INFERENCE_GREATER_CHAR +from lib.core.settings import INFERENCE_GREATER_EQUALS_CHAR from lib.core.settings import INFERENCE_EQUALS_CHAR +from lib.core.settings import INFERENCE_MARKER from lib.core.settings import INFERENCE_NOT_EQUALS_CHAR from lib.core.settings import MAX_BISECTION_LENGTH from lib.core.settings import MAX_REVALIDATION_STEPS @@ -67,7 +69,12 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None partialValue = u"" finalValue = None retrievedLength = 0 - asciiTbl = getCharset(charsetType) + + if charsetType is None and conf.charset: + asciiTbl = sorted(set(ord(_) for _ in conf.charset)) + else: + asciiTbl = getCharset(charsetType) + threadData = getCurrentThreadData() timeBasedCompare = (kb.technique in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)) retVal = hashDBRetrieve(expression, checkConf=True) @@ -109,7 +116,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None elif (kb.fileReadMode or dump) and conf.firstChar is not None and (isinstance(conf.firstChar, int) or (isinstance(conf.firstChar, basestring) and conf.firstChar.isdigit())): firstChar = int(conf.firstChar) - 1 if kb.fileReadMode: - firstChar *= 2 + firstChar <<= 1 elif isinstance(firstChar, basestring) and firstChar.isdigit() or isinstance(firstChar, int): firstChar = int(firstChar) - 1 else: @@ -271,7 +278,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None lastCheck = False unexpectedCode = False - while len(charTbl) != 1: + while len(charTbl) > 1: position = None if charsetType is None: @@ -444,23 +451,22 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None if threadData.shared.index[0] - firstChar >= length: kb.locks.index.release() - return threadData.shared.index[0] += 1 - curidx = threadData.shared.index[0] + currentCharIndex = threadData.shared.index[0] kb.locks.index.release() if kb.threadContinue: charStart = time.time() - val = getChar(curidx) + val = getChar(currentCharIndex, asciiTbl, not(charsetType is None and conf.charset)) if val is None: val = INFERENCE_UNKNOWN_CHAR else: break with kb.locks.value: - threadData.shared.value[curidx - 1 - firstChar] = val + threadData.shared.value[currentCharIndex - 1 - firstChar] = val currentValue = list(threadData.shared.value) if kb.threadContinue: @@ -488,10 +494,10 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None count += 1 if currentValue[i] is not None else 0 if startCharIndex > 0: - output = '..' + output[2:] + output = ".." + output[2:] if (endCharIndex - startCharIndex == conf.progressWidth) and (endCharIndex < length - 1): - output = output[:-2] + '..' + output = output[:-2] + ".." if conf.verbose in (1, 2) and not showEta and not conf.api: _ = count - firstChar @@ -549,7 +555,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None testValue = unescaper.escape("'%s'" % commonValue) if "'" not in commonValue else unescaper.escape("%s" % commonValue, quote=False) query = kb.injection.data[kb.technique].vector - query = agent.prefixQuery(query.replace("[INFERENCE]", "(%s)=%s" % (expressionUnescaped, testValue))) + query = agent.prefixQuery(query.replace(INFERENCE_MARKER, "(%s)%s%s" % (expressionUnescaped, INFERENCE_EQUALS_CHAR, testValue))) query = agent.suffixQuery(query) result = Request.queryPage(agent.payload(newValue=query), timeBasedCompare=timeBasedCompare, raise404=False) @@ -573,7 +579,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None testValue = unescaper.escape("'%s'" % commonPattern) if "'" not in commonPattern else unescaper.escape("%s" % commonPattern, quote=False) query = kb.injection.data[kb.technique].vector - query = agent.prefixQuery(query.replace("[INFERENCE]", "(%s)=%s" % (subquery, testValue))) + query = agent.prefixQuery(query.replace(INFERENCE_MARKER, "(%s)=%s" % (subquery, testValue))) query = agent.suffixQuery(query) result = Request.queryPage(agent.payload(newValue=query), timeBasedCompare=timeBasedCompare, raise404=False) @@ -594,9 +600,9 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None # If we had no luck with commonValue and common charset, # use the returned other charset if not val: - val = getChar(index, otherCharset, otherCharset == asciiTbl) + val = getChar(index, otherCharset, otherCharset==asciiTbl) else: - val = getChar(index, asciiTbl) + val = getChar(index, asciiTbl, not(charsetType is None and conf.charset)) if val is None: finalValue = partialValue diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index 88fcb4df4..79024c72d 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -46,7 +46,7 @@ class Filesystem(GenericFilesystem): scrString = "" for lineChar in fileContent[fileLine:fileLine + lineLen]: - strLineChar = hexencode(lineChar, conf.charset) + strLineChar = hexencode(lineChar, conf.encoding) if not scrString: scrString = "e %x %s" % (lineAddr, strLineChar) diff --git a/plugins/generic/users.py b/plugins/generic/users.py index deb0a4795..4b258787a 100644 --- a/plugins/generic/users.py +++ b/plugins/generic/users.py @@ -235,7 +235,7 @@ class Users: if retVal: for user, password in filterPairValues(zip(retVal[0]["%s.name" % randStr], retVal[0]["%s.password" % randStr])): - password = "0x%s" % hexencode(password, conf.charset).upper() + password = "0x%s" % hexencode(password, conf.encoding).upper() if user not in kb.data.cachedUsersPasswords: kb.data.cachedUsersPasswords[user] = [password] diff --git a/sqlmap.conf b/sqlmap.conf index faae80a98..77849ce71 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -671,9 +671,6 @@ batch = False # Result fields having binary values (e.g. "digest"). binaryFields = -# Force character encoding used for data retrieval. -charset = - # Check Internet connection before assessing the target. checkInternet = False @@ -693,6 +690,9 @@ csvDel = , # Valid: CSV, HTML or SQLITE dumpFormat = CSV +# Force character encoding used for data retrieval. +encoding = + # Retrieve each query output length and calculate the estimated time of # arrival in real time. # Valid: True or False diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 81a125515..cd35e170c 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -27,7 +27,7 @@ a66093c734c7f94ecdf94d882c2d8b89 lib/controller/controller.py 310efc965c862cfbd7b0da5150a5ad36 lib/controller/__init__.py 90b4f40ccde13c44e26f53db474afc19 lib/core/agent.py 6cc95a117fbd34ef31b9aa25520f0e31 lib/core/bigarray.py -8dafb651e35d90c05ffe08bf2a9832ae lib/core/common.py +ff068a628d68a4dcf597ae60e6e8abe2 lib/core/common.py 9edefb92b0b9cad862543fcd587aaa66 lib/core/convert.py a8143dab9d3a27490f7d49b6b29ea530 lib/core/data.py 7936d78b1a7f1f008ff92bf2f88574ba lib/core/datatype.py @@ -39,14 +39,14 @@ c8553b821a2089cb8ddd39ae661f25fc lib/core/enums.py a44d7a4cc6c9a67a72d6af2f25f4ddac lib/core/exception.py 310efc965c862cfbd7b0da5150a5ad36 lib/core/__init__.py 9ba39bf66e9ecd469446bdbbeda906c3 lib/core/log.py -9d7069d81e4a520ed3fbcac584c1e86e lib/core/optiondict.py -79700f211e6824f183c2f776134a8b92 lib/core/option.py +e8e9fd4f224ead0caa1569312b5b2582 lib/core/optiondict.py +1b22c491f07c838eb470624ee4320d12 lib/core/option.py 5f2f56e6c5f274408df61943f1e080c0 lib/core/profiling.py 40be71cd774662a7b420caeb7051e7d5 lib/core/readlineng.py d8e9250f3775119df07e9070eddccd16 lib/core/replication.py 785f86e3f963fa3798f84286a4e83ff2 lib/core/revision.py 40c80b28b3a5819b737a5a17d4565ae9 lib/core/session.py -64dbe305e1d88e9b90e66829b183a75d lib/core/settings.py +bc83df99ea0febcf872e078408a71c8f lib/core/settings.py d91291997d2bd2f6028aaf371bf1d3b6 lib/core/shell.py 2ad85c130cc5f2b3701ea85c2f6bbf20 lib/core/subprocessng.py effc153067a00bd43461bfc1cdec1122 lib/core/target.py @@ -57,7 +57,7 @@ ad74fc58fc7214802fd27067bce18dd2 lib/core/unescaper.py 4d13ed693401a498b6d073a2a494bd83 lib/core/wordlist.py 310efc965c862cfbd7b0da5150a5ad36 lib/__init__.py 8c4b04062db2245d9e190b413985202a lib/parse/banner.py -96a762a2ed36b34baedd59b9bfae80c2 lib/parse/cmdline.py +850041c049ae5a8f4a318680e1ef812d lib/parse/cmdline.py 3a31657bc38f277d0016ff6d50bde61f lib/parse/configfile.py 14539f1be714d4f1ed042067d63bc50a lib/parse/handler.py 8da3684e70bfeef80b1d221ff0cd958c lib/parse/headers.py @@ -66,7 +66,7 @@ ad74fc58fc7214802fd27067bce18dd2 lib/core/unescaper.py 0b010b7cdb2e42b5aa0caa59607279ad lib/parse/payloads.py 997d0452e6fc22411f81a334511bcb3d lib/parse/sitemap.py 403d873f1d2fd0c7f73d83f104e41850 lib/request/basicauthhandler.py -a06eddbdb529d4253c57250decb8e960 lib/request/basic.py +0c476bde96ad035b3f0dde3b845e5e6e lib/request/basic.py ef48de622b0a6b4a71df64b0d2785ef8 lib/request/comparison.py 1ec370ec9d037135607b48ad6afd4f40 lib/request/connect.py fb6b788d0016ab4ec5e5f661f0f702ad lib/request/direct.py @@ -85,9 +85,9 @@ c6bc7961a186baabe0a9f5b7e0d8974b lib/takeover/icmpsh.py c90c993b020a6ae0f0e497fd84f37466 lib/takeover/metasploit.py ac541a0d38e4ecb4e41e97799a7235f4 lib/takeover/registry.py ff1af7f85fdf4f2a5369f2927d149824 lib/takeover/udf.py -8ecd543e41709ff48e0ff7bed0f2a5a9 lib/takeover/web.py -9aa222e644db8d129b65ff2f119fc7f5 lib/takeover/xp_cmdshell.py -201e7e69f9161dfa3aa10d83f690a488 lib/techniques/blind/inference.py +8df5a334823b724a2207a28c94f6fe3d lib/takeover/web.py +b4c3264b9b6dcbf00cb7bffa447d1f6c lib/takeover/xp_cmdshell.py +988677fd3625518b771566b407adfef6 lib/techniques/blind/inference.py 310efc965c862cfbd7b0da5150a5ad36 lib/techniques/blind/__init__.py 310efc965c862cfbd7b0da5150a5ad36 lib/techniques/dns/__init__.py ab1601a7f429b47637c4fb8af703d0f1 lib/techniques/dns/test.py @@ -161,7 +161,7 @@ e6036f5b2e39aec37ba036a8cf0efd6f plugins/dbms/maxdb/syntax.py 0be362015605e26551e5d79cc83ed466 plugins/dbms/maxdb/takeover.py 9b3a681ff4087824fb43e23679057fa3 plugins/dbms/mssqlserver/connector.py b8de437eaa3e05c3db666968b7d142e4 plugins/dbms/mssqlserver/enumeration.py -2129d6c7af5b40a58fe71f8bb49eb80b plugins/dbms/mssqlserver/filesystem.py +77db701972e01e50a61be528e6cad6e2 plugins/dbms/mssqlserver/filesystem.py 5207943c31e166a70d5fc7cec8b5ef18 plugins/dbms/mssqlserver/fingerprint.py 40bd890988f9acd3942255d687445371 plugins/dbms/mssqlserver/__init__.py 400ce654ff6bc57a40fb291322a18282 plugins/dbms/mssqlserver/syntax.py @@ -213,7 +213,7 @@ f700954549ad8ebf77f5187262fb9af0 plugins/generic/connector.py 070f58c52e2a04e7a9896b42b2d17dc2 plugins/generic/search.py 562cfa80a15d5f7f1d52e10c5736d7e2 plugins/generic/syntax.py fca9946e960942cc9b22ef26e12b8b3a plugins/generic/takeover.py -841c785748c22ad0f5e7af2f5cf7fd6e plugins/generic/users.py +f5c3c5da9ef0f265dc28becbf75ff5e9 plugins/generic/users.py 310efc965c862cfbd7b0da5150a5ad36 plugins/__init__.py b04db3e861edde1f9dd0a3850d5b96c8 shell/backdoor.asp_ 158bfa168128393dde8d6ed11fe9a1b8 shell/backdoor.aspx_