Skip to content

Commit 21d9ae0

Browse files
committed
some more refactoring
1 parent f6509db commit 21d9ae0

3 files changed

Lines changed: 50 additions & 91 deletions

File tree

‎lib/core/common.py‎

Lines changed: 43 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"""
77

88
import codecs
9+
import contextlib
910
import cookielib
1011
import copy
1112
import ctypes
@@ -102,14 +103,10 @@
102103
from lib.core.settings import PARTIAL_VALUE_MARKER
103104
from lib.core.settings import ERROR_PARSING_REGEXES
104105
from lib.core.settings import PRINTABLE_CHAR_REGEX
105-
from lib.core.settings import DUMP_DEL_MARKER
106106
from lib.core.settings import SQL_STATEMENTS
107107
from lib.core.settings import SUPPORTED_DBMS
108108
from lib.core.settings import UNKNOWN_DBMS_VERSION
109109
from lib.core.settings import DEFAULT_MSSQL_SCHEMA
110-
from lib.core.settings import DUMP_NEWLINE_MARKER
111-
from lib.core.settings import DUMP_CR_MARKER
112-
from lib.core.settings import DUMP_TAB_MARKER
113110
from lib.core.settings import PARAMETER_AMP_MARKER
114111
from lib.core.settings import PARAMETER_SEMICOLON_MARKER
115112
from lib.core.settings import LARGE_OUTPUT_THRESHOLD
@@ -492,27 +489,17 @@ def isOs(os):
492489

493490
# Reference: http://code.activestate.com/recipes/325205-cache-decorator-in-python-24/
494491
def cachedmethod(f, cache={}):
495-
def g(*args, **kwargs):
492+
def _(*args, **kwargs):
496493
key = (f, tuple(args), frozenset(kwargs.items()))
497494
if key not in cache:
498495
cache[key] = f(*args, **kwargs)
499496
return cache[key]
500-
return g
497+
return _
501498

502499
def paramToDict(place, parameters=None):
503500
"""
504501
Split the parameters into names and values, check if these parameters
505502
are within the testable parameters and return in a dictionary.
506-
507-
@param place: where sqlmap has to work, can be GET, POST or Cookie.
508-
@type place: C{str}
509-
510-
@param parameters: parameters string in the format for instance
511-
'p1=v1&p2=v2' (GET and POST) or 'p1=v1;p2=v2' (Cookie).
512-
@type parameters: C{str}
513-
514-
@return: the parameters in a dictionary.
515-
@rtype: C{str}
516503
"""
517504

518505
testableParameters = OrderedDict()
@@ -706,6 +693,10 @@ def singleTimeLogMessage(message, level=logging.INFO, flag=None):
706693
logger.log(level, message)
707694

708695
def dataToStdout(data, forceOutput=False):
696+
"""
697+
Writes text to the stdout (console) stream
698+
"""
699+
709700
if not kb.get("threadException"):
710701
if forceOutput or not getCurrentThreadData().disableStdOut:
711702
try:
@@ -793,13 +784,10 @@ def readInput(message, default=None, checkBatch=True):
793784
else:
794785
logging._acquireLock()
795786
dataToStdout("\r%s" % message, True)
796-
data = raw_input()
787+
data = raw_input() or default
797788
#data = raw_input(message.encode(sys.stdout.encoding or UNICODE_ENCODING))
798789
logging._releaseLock()
799790

800-
if not data:
801-
data = default
802-
803791
return data
804792

805793
def randomRange(start=0, stop=1000):
@@ -845,19 +833,6 @@ def checkFile(filename):
845833
if not os.path.exists(filename):
846834
raise sqlmapFilePathException, "unable to read file '%s'" % filename
847835

848-
def replaceNewlineTabs(value, stdout=False):
849-
if value is None:
850-
return
851-
852-
if stdout:
853-
retVal = value.replace("\n", " ").replace("\r", " ").replace("\t", " ")
854-
else:
855-
retVal = value.replace("\n", DUMP_NEWLINE_MARKER).replace("\r", DUMP_CR_MARKER).replace("\t", DUMP_TAB_MARKER)
856-
857-
retVal = retVal.replace(kb.chars.delimiter, DUMP_DEL_MARKER)
858-
859-
return retVal
860-
861836
def restoreDumpMarkedChars(value, onlyNewlineTab=False):
862837
retVal = value
863838

@@ -1070,16 +1045,15 @@ def parseTargetUrl():
10701045
if CUSTOM_INJECTION_MARK_CHAR in conf.url:
10711046
conf.url = conf.url.replace('?', URI_QUESTION_MARKER)
10721047

1073-
__urlSplit = urlparse.urlsplit(conf.url)
1074-
__hostnamePort = __urlSplit[1].split(":") if not re.search("\[.+\]", __urlSplit[1]) else filter(None, (re.search("\[.+\]", __urlSplit[1]).group(0), re.search("\](:(?P<port>\d+))?", __urlSplit[1]).group("port")))
1048+
urlSplit = urlparse.urlsplit(conf.url)
1049+
hostnamePort = urlSplit[1].split(":") if not re.search("\[.+\]", urlSplit[1]) else filter(None, (re.search("\[.+\]", urlSplit[1]).group(0), re.search("\](:(?P<port>\d+))?", urlSplit[1]).group("port")))
10751050

1076-
conf.scheme = __urlSplit[0].strip().lower() if not conf.forceSSL else "https"
1077-
conf.path = __urlSplit[2].strip()
1078-
conf.hostname = __hostnamePort[0].strip()
1051+
conf.scheme = urlSplit[0].strip().lower() if not conf.forceSSL else "https"
1052+
conf.path = urlSplit[2].strip()
1053+
conf.hostname = hostnamePort[0].strip()
10791054

10801055
conf.ipv6 = conf.hostname != conf.hostname.strip("[]")
10811056
conf.hostname = conf.hostname.strip("[]")
1082-
10831057

10841058
try:
10851059
_ = conf.hostname.encode("idna")
@@ -1090,9 +1064,9 @@ def parseTargetUrl():
10901064
errMsg = "invalid target url"
10911065
raise sqlmapSyntaxException, errMsg
10921066

1093-
if len(__hostnamePort) == 2:
1067+
if len(hostnamePort) == 2:
10941068
try:
1095-
conf.port = int(__hostnamePort[1])
1069+
conf.port = int(hostnamePort[1])
10961070
except:
10971071
errMsg = "invalid target url"
10981072
raise sqlmapSyntaxException, errMsg
@@ -1101,8 +1075,8 @@ def parseTargetUrl():
11011075
else:
11021076
conf.port = 80
11031077

1104-
if __urlSplit[3]:
1105-
conf.parameters[PLACE.GET] = urldecode(__urlSplit[3]) if __urlSplit[3] and urlencode(DEFAULT_GET_POST_DELIMITER, None) not in __urlSplit[3] else __urlSplit[3]
1078+
if urlSplit[3]:
1079+
conf.parameters[PLACE.GET] = urldecode(urlSplit[3]) if urlSplit[3] and urlencode(DEFAULT_GET_POST_DELIMITER, None) not in urlSplit[3] else urlSplit[3]
11061080

11071081
conf.url = "%s://%s:%d%s" % (conf.scheme, ("[%s]" % conf.hostname) if conf.ipv6 else conf.hostname, conf.port, conf.path)
11081082
conf.url = conf.url.replace(URI_QUESTION_MARKER, '?')
@@ -1349,24 +1323,14 @@ def directoryPath(filepath):
13491323
Returns directory path for a given filepath
13501324
"""
13511325

1352-
if isWindowsDriveLetterPath(filepath):
1353-
retVal = ntpath.dirname(filepath)
1354-
else:
1355-
retVal = posixpath.dirname(filepath)
1356-
1357-
return retVal
1326+
return ntpath.dirname(filepath) if isWindowsDriveLetterPath(filepath) else posixpath.dirname(filepath)
13581327

13591328
def normalizePath(filepath):
13601329
"""
13611330
Returns normalized string representation of a given filepath
13621331
"""
13631332

1364-
if isWindowsDriveLetterPath(filepath):
1365-
retVal = ntpath.normpath(filepath)
1366-
else:
1367-
retVal = posixpath.normpath(filepath)
1368-
1369-
return retVal
1333+
return ntpath.normpath(filepath) if isWindowsDriveLetterPath(filepath) else posixpath.normpath(filepath)
13701334

13711335
def safeStringFormat(format_, params):
13721336
"""
@@ -1379,16 +1343,13 @@ def safeStringFormat(format_, params):
13791343
retVal = retVal.replace("%s", params)
13801344
else:
13811345
count, index = 0, 0
1382-
13831346
while index != -1:
13841347
index = retVal.find("%s")
1385-
13861348
if index != -1:
13871349
if count < len(params):
13881350
retVal = retVal[:index] + getUnicode(params[count]) + retVal[index + 2:]
13891351
else:
13901352
raise sqlmapNoneDataException, "wrong number of parameters during string formatting"
1391-
13921353
count += 1
13931354

13941355
return retVal
@@ -1404,10 +1365,8 @@ def getFilteredPageContent(page, onlyText=True):
14041365
# only if the page's charset has been successfully identified
14051366
if isinstance(page, unicode):
14061367
retVal = re.sub(r"(?si)<script.+?</script>|<!--.+?-->|<style.+?</style>%s" % (r"|<[^>]+>|\t|\n|\r" if onlyText else ""), " ", page)
1407-
14081368
while retVal.find(" ") != -1:
14091369
retVal = retVal.replace(" ", " ")
1410-
14111370
retVal = htmlunescape(retVal)
14121371

14131372
return retVal
@@ -1421,8 +1380,8 @@ def getPageWordSet(page):
14211380

14221381
# only if the page's charset has been successfully identified
14231382
if isinstance(page, unicode):
1424-
page = getFilteredPageContent(page)
1425-
retVal = set(re.findall(r"\w+", page))
1383+
_ = getFilteredPageContent(page)
1384+
retVal = set(re.findall(r"\w+", _))
14261385

14271386
return retVal
14281387

@@ -1557,8 +1516,8 @@ def getConsoleWidth(default=80):
15571516

15581517
width = None
15591518

1560-
if 'COLUMNS' in os.environ and os.environ['COLUMNS'].isdigit():
1561-
width = int(os.environ['COLUMNS'])
1519+
if os.getenv("COLUMNS", "").isdigit():
1520+
width = int(os.getenv("COLUMNS"))
15621521
else:
15631522
output=execute('stty size', shell=True, stdout=PIPE, stderr=PIPE).stdout.read()
15641523
items = output.split()
@@ -1576,7 +1535,7 @@ def getConsoleWidth(default=80):
15761535
except:
15771536
pass
15781537

1579-
return width if width else default
1538+
return width or default
15801539

15811540
def clearConsoleLine(forceOutput=False):
15821541
"""
@@ -1591,7 +1550,7 @@ def parseXmlFile(xmlFile, handler):
15911550
Parses XML file by a given handler
15921551
"""
15931552

1594-
with StringIO(readCachedFileContent(xmlFile)) as stream:
1553+
with contextlib.closing(StringIO(readCachedFileContent(xmlFile))) as stream:
15951554
parse(stream, handler)
15961555

15971556
def getSPQLSnippet(dbms, name, **variables):
@@ -1632,8 +1591,7 @@ def readCachedFileContent(filename, mode='rb'):
16321591
if filename not in kb.cache.content:
16331592
checkFile(filename)
16341593
with codecs.open(filename, mode, UNICODE_ENCODING) as f:
1635-
content = f.read()
1636-
kb.cache.content[filename] = content
1594+
kb.cache.content[filename] = f.read()
16371595

16381596
return kb.cache.content[filename]
16391597

@@ -1675,12 +1633,7 @@ def average(values):
16751633
Computes the arithmetic mean of a list of numbers.
16761634
"""
16771635

1678-
retVal = None
1679-
1680-
if values:
1681-
retVal = sum(values) / len(values)
1682-
1683-
return retVal
1636+
return (sum(values) / len(values)) if values else None
16841637

16851638
def calculateDeltaSeconds(start):
16861639
"""
@@ -1891,8 +1844,8 @@ def getUnicode(value, encoding=None, system=False, noneToNull=False):
18911844
def longestCommonPrefix(*sequences):
18921845
"""
18931846
Returns longest common prefix occuring in given sequences
1847+
Reference: http://boredzo.org/blog/archives/2007-01-06/longest-common-prefix-in-python-2
18941848
"""
1895-
# Reference: http://boredzo.org/blog/archives/2007-01-06/longest-common-prefix-in-python-2
18961849

18971850
if len(sequences) == 1:
18981851
return sequences[0]
@@ -2356,6 +2309,10 @@ def isInferenceAvailable():
23562309
return any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.BOOLEAN, PAYLOAD.TECHNIQUE.STACKED, PAYLOAD.TECHNIQUE.TIME))
23572310

23582311
def setOptimize():
2312+
"""
2313+
Sets options turned on by switch '-o'
2314+
"""
2315+
23592316
#conf.predictOutput = True
23602317
conf.keepAlive = True
23612318
conf.threads = 3 if conf.threads < 3 else conf.threads
@@ -2719,7 +2676,7 @@ def safeSQLIdentificatorNaming(name, isTable=False):
27192676
parts = name.split('.')
27202677

27212678
for i in xrange(len(parts)):
2722-
if not re.match(r"\A[A-Za-z0-9_@\$]+\Z", parts[i]): # reference: http://stackoverflow.com/questions/954884/what-special-characters-are-allowed-in-t-sql-column-name
2679+
if not re.match(r"\A[A-Za-z0-9_@\$]+\Z", parts[i]): # Reference: http://stackoverflow.com/questions/954884/what-special-characters-are-allowed-in-t-sql-column-name
27232680
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS):
27242681
parts[i] = "`%s`" % parts[i].strip("`")
27252682
elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.PGSQL, DBMS.DB2):
@@ -3082,10 +3039,7 @@ def unserializeObject(value):
30823039
Unserializes object from given serialized form
30833040
"""
30843041

3085-
retVal = None
3086-
if value:
3087-
retVal = base64unpickle(value)
3088-
return retVal
3042+
return base64unpickle(value) if value else None
30893043

30903044
def resetCounter(technique):
30913045
"""
@@ -3176,10 +3130,7 @@ def extractExpectedValue(value, expected):
31763130
value = None
31773131
elif expected == EXPECTED.INT:
31783132
if isinstance(value, basestring):
3179-
if value.isdigit():
3180-
value = int(value)
3181-
else:
3182-
value = None
3133+
value = int(value) if value.isdigit() else None
31833134

31843135
return value
31853136

@@ -3211,6 +3162,10 @@ def hashDBRetrieve(key, unserialize=False, checkConf=False):
32113162
return _
32123163

32133164
def resetCookieJar(cookieJar):
3165+
"""
3166+
Cleans cookies from a given cookie jar
3167+
"""
3168+
32143169
if not conf.loC:
32153170
cookieJar.clear()
32163171
else:
@@ -3223,5 +3178,9 @@ def resetCookieJar(cookieJar):
32233178
raise sqlmapGenericException, errMsg
32243179

32253180
def prioritySortColumns(columns):
3181+
"""
3182+
Sorts given column names by length in ascending order while those containing
3183+
string 'id' go first
3184+
"""
32263185
_ = lambda x: x and "id" in x.lower()
32273186
return sorted(sorted(columns, key=len), lambda x, y: -1 if _(x) and not _(y) else 1 if not _(x) and _(y) else 0)

‎lib/core/settings.py‎

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77

88
import logging
99
import os
10+
import re
1011
import subprocess
12+
import string
1113
import sys
1214

1315
from lib.core.enums import CUSTOM_LOGGING
@@ -44,12 +46,6 @@
4446
LOGGER.addHandler(LOGGER_HANDLER)
4547
LOGGER.setLevel(logging.WARN)
4648

47-
# dump markers
48-
DUMP_NEWLINE_MARKER = "__NEWLINE__"
49-
DUMP_CR_MARKER = "__CARRIAGE_RETURN__"
50-
DUMP_TAB_MARKER = "__TAB__"
51-
DUMP_DEL_MARKER = "__DEL__"
52-
5349
# markers for special cases when parameter values contain html encoded characters
5450
PARAMETER_AMP_MARKER = "__AMP__"
5551
PARAMETER_SEMICOLON_MARKER = "__SEMICOLON__"
@@ -475,6 +471,9 @@
475471
# Reference: http://www.tcpipguide.com/free/t_DNSLabelsNamesandSyntaxRules.htm
476472
MAX_DNS_LABEL = 63
477473

474+
# Alphabet used for prefix and suffix strings of name resolution requests in DNS technique (excluding hexadecimal chars for not mixing with inner content)
475+
DNS_BOUNDARIES_ALPHABET = re.sub("[a-fA-F]", "", string.letters)
476+
478477
# Connection chunk size (processing large responses in chunks to avoid MemoryError crashes - e.g. large table dump in full UNION/inband injections)
479478
MAX_CONNECTION_CHUNK_SIZE = 10 * 1024 * 1024
480479

‎lib/techniques/dns/use.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
from lib.core.data import logger
3030
from lib.core.data import queries
3131
from lib.core.enums import DBMS
32+
from lib.core.settings import DNS_BOUNDARIES_ALPHABET
3233
from lib.core.settings import MAX_DNS_LABEL
3334
from lib.core.settings import PARTIAL_VALUE_MARKER
3435
from lib.core.unescaper import unescaper
@@ -58,7 +59,7 @@ def dnsUse(payload, expression):
5859

5960
while True:
6061
count += 1
61-
prefix, suffix = ("%s" % randomStr(length=3, alphabet=re.sub("[a-fA-F]", "", string.letters)) for _ in xrange(2))
62+
prefix, suffix = ("%s" % randomStr(length=3, alphabet=DNS_BOUNDARIES_ALPHABET) for _ in xrange(2))
6263
chunk_length = MAX_DNS_LABEL / 2 if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.MYSQL, DBMS.PGSQL) else MAX_DNS_LABEL / 4 - 2
6364
_, _, _, _, _, _, fieldToCastStr, _ = agent.getFields(expression)
6465
nulledCastedField = agent.nullAndCastField(fieldToCastStr)

0 commit comments

Comments
 (0)