Added support to directly connect also to Microsoft SQL Server database.

Fixed direct connection to always use the same query as of UNION query SQL injection (= one query with multiple columns/entries output).
Minor fixes to Firebird/Access/SQLite connectors to use connector's execute()/fetchall() as wrapper for third-party libraries' methods.
Forced conf.timeout to 10 seconds when directly connecting to database.
Slightly improved regular expression to parse -d parameter.
Added import check for all connectors' third-party libraries.
Code refactoring:
* Moved conf.direct request to direct() function in lib/request/direct.py (code reused where needed).
* Back-delegated to generic connector close() and other methods.
This commit is contained in:
Bernardo Damele
2010-03-31 10:50:47 +00:00
parent d583cc07e7
commit 5fdebb5d5b
22 changed files with 205 additions and 223 deletions

View File

@@ -45,15 +45,14 @@ class Connector(GenericConnector):
def __init__(self):
GenericConnector.__init__(self)
def connect(self, reuse=True):
if reuse and self.connector:
return
def connect(self):
self.initConnection()
self.checkFileDb()
try:
#self.connector = pyodbc.connect(driver='{Microsoft Access Driver (*.mdb)}', dbq=self.db, uid='Admin')
self.connector = pyodbc.connect('Driver={Microsoft Access Driver (*.mdb)};Dbq=%s;Uid=Admin;Pwd=;' % self.db)
self.connector.timeout = conf.timeout
except pyodbc.OperationalError, msg:
raise sqlmapConnectionException, msg[1]
@@ -63,7 +62,7 @@ class Connector(GenericConnector):
def fetchall(self):
try:
return self.cursor.fetchall()
except pyodbc.OperationalError, msg:
except pyodbc.ProgrammingError, msg:
logger.log(8, msg[1])
return None
@@ -80,18 +79,5 @@ class Connector(GenericConnector):
self.connector.commit()
def select(self, query):
try:
self.cursor.execute(query)
return self.cursor.fetchall()
except pyodbc.ProgrammingError, msg:
logger.log(8, msg[1])
return None
def setCursor(self):
self.cursor = self.connector.cursor()
def close(self):
self.cursor.close()
self.connector.close()
self.closed()
self.execute(query)
return self.fetchall()

View File

@@ -140,6 +140,7 @@ class Fingerprint(GenericFingerprint):
def checkDbms(self):
if conf.dbms in ACCESS_ALIASES:
setDbms("Microsoft Access")
if not conf.extensiveFp:
return True

View File

@@ -37,20 +37,21 @@ class Connector(GenericConnector):
"""
Homepage: http://kinterbasdb.sourceforge.net/
User guide: http://kinterbasdb.sourceforge.net/dist_docs/usage.html
Debian package: python-kinterbasdb
License: BSD
"""
def __init__(self):
GenericConnector.__init__(self)
def connect(self, reuse=True):
if reuse and self.connector:
return
def connect(self):
self.initConnection()
if not self.hostname:
self.checkFileDb()
try:
self.connector = kinterbasdb.connect(database=self.db, user=self.user, password=self.password, timeout={'period': conf.timeout})
self.connector = kinterbasdb.connect(host=self.hostname, database=self.db, user=self.user, password=self.password, timeout={'period': conf.timeout})
except kinterbasdb.OperationalError, msg:
raise sqlmapConnectionException, msg[1]
@@ -77,13 +78,5 @@ class Connector(GenericConnector):
self.connector.commit()
def select(self, query):
self.cursor.execute(query)
return self.cursor.fetchall()
def setCursor(self):
self.cursor = self.connector.cursor()
def close(self):
self.cursor.close()
self.connector.close()
self.closed()
self.execute(query)
return self.fetchall()

View File

@@ -23,10 +23,12 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
try:
import _mssql
import pymssql
except ImportError, _:
pass
from lib.core.data import conf
from lib.core.data import logger
from lib.core.exception import sqlmapConnectionException
@@ -45,3 +47,38 @@ class Connector(GenericConnector):
def __init__(self):
GenericConnector.__init__(self)
def connect(self):
self.initConnection()
try:
self.connector = pymssql.connect(host="%s:%d" % (self.hostname, self.port), user=self.user, password=self.password, database=self.db, login_timeout=conf.timeout, timeout=conf.timeout)
except pymssql.OperationalError, msg:
raise sqlmapConnectionException, msg
self.setCursor()
self.connected()
def fetchall(self):
try:
return self.cursor.fetchall()
except (pymssql.ProgrammingError, pymssql.OperationalError, _mssql.MssqlDatabaseException), msg:
logger.log(8, msg)
return None
def execute(self, query):
logger.debug(query)
try:
self.cursor.execute(query)
except (pymssql.OperationalError, pymssql.ProgrammingError), msg:
logger.log(8, msg)
except pymssql.InternalError, msg:
raise sqlmapConnectionException, msg
def select(self, query):
self.execute(query)
value = self.fetchall()
self.connector.commit()
return value

View File

@@ -61,7 +61,7 @@ class Enumeration(GenericEnumeration):
else:
dbs = [conf.db]
if kb.unionPosition:
if kb.unionPosition or conf.direct:
for db in dbs:
if conf.excludeSysDbs and db in self.excludeDbsList:
infoMsg = "skipping system database '%s'" % db
@@ -75,7 +75,7 @@ class Enumeration(GenericEnumeration):
if value:
kb.data.cachedTables[db] = value
if not kb.data.cachedTables:
if not kb.data.cachedTables and not conf.direct:
for db in dbs:
if conf.excludeSysDbs and db in self.excludeDbsList:
infoMsg = "skipping system database '%s'" % db

View File

@@ -97,8 +97,13 @@ class Fingerprint(GenericFingerprint):
infoMsg = "testing Microsoft SQL Server"
logger.info(infoMsg)
payload = agent.fullPayload(" AND LEN(@@VERSION)=LEN(@@VERSION)")
result = Request.queryPage(payload)
# NOTE: SELECT LEN(@@VERSION)=LEN(@@VERSION) FROM DUAL does not work connecting
# directly to the Microsoft SQL Server database
if conf.direct:
result = True
else:
payload = agent.fullPayload(" AND LEN(@@VERSION)=LEN(@@VERSION)")
result = Request.queryPage(payload)
if result:
infoMsg = "confirming Microsoft SQL Server"

View File

@@ -47,10 +47,7 @@ class Connector(GenericConnector):
def __init__(self):
GenericConnector.__init__(self)
def connect(self, reuse=True):
if reuse and self.connector:
return
def connect(self):
self.initConnection()
try:
@@ -83,11 +80,3 @@ class Connector(GenericConnector):
def select(self, query):
self.execute(query)
return self.fetchall()
def setCursor(self):
self.cursor = self.connector.cursor()
def close(self):
self.cursor.close()
self.connector.close()
self.closed()

View File

@@ -154,9 +154,6 @@ class Fingerprint(GenericFingerprint):
* http://dev.mysql.com/doc/refman/6.0/en/news-6-0-x.html (manual has been withdrawn)
"""
infoMsg = "testing MySQL"
logger.info(infoMsg)
if conf.dbms in MYSQL_ALIASES and kb.dbmsVersion and kb.dbmsVersion[0].isdigit():
setDbms("MySQL %s" % kb.dbmsVersion[0])
@@ -168,6 +165,9 @@ class Fingerprint(GenericFingerprint):
if not conf.extensiveFp:
return True
infoMsg = "testing MySQL"
logger.info(infoMsg)
randInt = str(randomInt(1))
payload = agent.fullPayload(" AND CONNECTION_ID()=CONNECTION_ID()")
result = Request.queryPage(payload)

View File

@@ -37,19 +37,13 @@ class Connector(GenericConnector):
Homepage: http://cx-oracle.sourceforge.net/
User guide: http://cx-oracle.sourceforge.net/README.txt
API: http://cx-oracle.sourceforge.net/html/index.html
Debian package: -
License: http://cx-oracle.sourceforge.net/LICENSE.txt
Possible connectors: -
"""
def __init__(self):
GenericConnector.__init__(self)
def connect(self, reuse=True):
if reuse and self.connector:
return
def connect(self):
self.initConnection()
self.__dsn = cx_Oracle.makedsn(self.hostname, self.port, self.db)
@@ -87,11 +81,3 @@ class Connector(GenericConnector):
def select(self, query):
self.execute(query)
return self.fetchall()
def setCursor(self):
self.cursor = self.connector.cursor()
def close(self):
self.cursor.close()
self.connector.close()
self.closed()

View File

@@ -96,7 +96,7 @@ class Enumeration(GenericEnumeration):
else:
kb.data.cachedUsersRoles[user] = list(roles)
if not kb.data.cachedUsersRoles:
if not kb.data.cachedUsersRoles and not conf.direct:
conditionChar = "="
if conf.user:

View File

@@ -78,9 +78,6 @@ class Fingerprint(GenericFingerprint):
return value
def checkDbms(self):
logMsg = "testing Oracle"
logger.info(logMsg)
if conf.dbms in ORACLE_ALIASES:
setDbms("Oracle")
@@ -89,6 +86,9 @@ class Fingerprint(GenericFingerprint):
if not conf.extensiveFp:
return True
logMsg = "testing Oracle"
logger.info(logMsg)
# NOTE: SELECT ROWNUM=ROWNUM FROM DUAL does not work connecting
# directly to the Oracle database
if conf.direct:

View File

@@ -46,10 +46,7 @@ class Connector(GenericConnector):
def __init__(self):
GenericConnector.__init__(self)
def connect(self, reuse=True):
if reuse and self.connector:
return
def connect(self):
self.initConnection()
try:
@@ -82,11 +79,3 @@ class Connector(GenericConnector):
def select(self, query):
self.execute(query)
return self.fetchall()
def setCursor(self):
self.cursor = self.connector.cursor()
def close(self):
self.cursor.close()
self.connector.close()
self.closed()

View File

@@ -86,9 +86,6 @@ class Fingerprint(GenericFingerprint):
* http://www.postgresql.org/docs/8.4/interactive/release.html (up to 8.4.2)
"""
infoMsg = "testing PostgreSQL"
logger.info(infoMsg)
if conf.dbms in PGSQL_ALIASES:
setDbms("PostgreSQL")
@@ -97,6 +94,9 @@ class Fingerprint(GenericFingerprint):
if not conf.extensiveFp:
return True
infoMsg = "testing PostgreSQL"
logger.info(infoMsg)
randInt = str(randomInt(1))
payload = agent.fullPayload(" AND %s::int=%s" % (randInt, randInt))

View File

@@ -39,7 +39,7 @@ class Connector(GenericConnector):
User guide: http://docs.python.org/release/2.5/lib/module-sqlite3.html
API: http://docs.python.org/library/sqlite3.html
Debian package: python-pysqlite2
License: zlib/libpng
License: MIT
Possible connectors: http://wiki.python.org/moin/SQLite
"""
@@ -47,11 +47,9 @@ class Connector(GenericConnector):
def __init__(self):
GenericConnector.__init__(self)
def connect(self, reuse=True):
if reuse and self.connector:
return
def connect(self):
self.initConnection()
self.checkFileDb()
try:
self.connector = sqlite3.connect(database=self.db, timeout=conf.timeout)
@@ -75,19 +73,11 @@ class Connector(GenericConnector):
self.cursor.execute(query)
except sqlite3.OperationalError, msg:
logger.log(8, msg[0])
except sqlite3.Error, msg:
except sqlite3.DatabaseError, msg:
raise sqlmapConnectionException, msg[0]
self.connector.commit()
def select(self, query):
self.cursor.execute(query)
return self.cursor.fetchall()
def setCursor(self):
self.cursor = self.connector.cursor()
def close(self):
self.cursor.close()
self.connector.close()
self.closed()
self.execute(query)
return self.fetchall()

View File

@@ -22,8 +22,11 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51
Franklin St, Fifth Floor, Boston, MA 021101301 USA
"""
import os
from lib.core.data import conf
from lib.core.data import logger
from lib.core.exception import sqlmapFilePathException
from lib.core.exception import sqlmapUndefinedMethod
class Connector:
@@ -48,12 +51,29 @@ class Connector:
logger.info(infoMsg)
def closed(self):
self.connector = None
self.cursor = None
infoMsg = "connection to %s server %s" % (conf.dbms, self.hostname)
infoMsg += ":%d closed" % self.port
logger.info(infoMsg)
self.connector = None
self.cursor = None
def setCursor(self):
self.cursor = self.connector.cursor()
def getCursor(self):
return self.cursor
def close(self):
self.cursor.close()
self.connector.close()
self.closed()
def checkFileDb(self):
if not os.path.exists(self.db):
errMsg = "the provided database file '%s' does not exist" % self.db
raise sqlmapFilePathException, errMsg
def connect(self):
errMsg = "'connect' method must be defined "
errMsg += "into the specific DBMS plugin"
@@ -73,16 +93,3 @@ class Connector:
errMsg = "'select' method must be defined "
errMsg += "into the specific DBMS plugin"
raise sqlmapUndefinedMethod, errMsg
def setCursor(self):
errMsg = "'setCursor' method must be defined "
errMsg += "into the specific DBMS plugin"
raise sqlmapUndefinedMethod, errMsg
def getCursor(self):
return self.cursor
def close(self):
errMsg = "'close' method must be defined "
errMsg += "into the specific DBMS plugin"
raise sqlmapUndefinedMethod, errMsg

View File

@@ -149,7 +149,7 @@ class Enumeration:
if value:
kb.data.cachedUsers = value
if not kb.data.cachedUsers:
if not kb.data.cachedUsers and not conf.direct:
infoMsg = "fetching number of database users"
logger.info(infoMsg)
@@ -232,7 +232,7 @@ class Enumeration:
else:
kb.data.cachedUsersPasswords[user].append(password)
if not kb.data.cachedUsersPasswords:
if not kb.data.cachedUsersPasswords and not conf.direct:
if conf.user:
if "," in conf.user:
users = conf.user.split(",")
@@ -464,7 +464,7 @@ class Enumeration:
else:
kb.data.cachedUsersPrivileges[user] = list(privileges)
if not kb.data.cachedUsersPrivileges:
if not kb.data.cachedUsersPrivileges and not conf.direct:
conditionChar = "="
if conf.user:
@@ -649,7 +649,7 @@ class Enumeration:
if value:
kb.data.cachedDbs = value
if not kb.data.cachedDbs:
if not kb.data.cachedDbs and not conf.direct:
infoMsg = "fetching number of databases"
logger.info(infoMsg)
@@ -733,7 +733,7 @@ class Enumeration:
else:
kb.data.cachedTables[db].append(table)
if not kb.data.cachedTables:
if not kb.data.cachedTables and not conf.direct:
if conf.db:
if "," in conf.db:
dbs = conf.db.split(",")
@@ -881,7 +881,7 @@ class Enumeration:
table[conf.tbl] = columns
kb.data.cachedColumns[conf.db] = table
if not kb.data.cachedColumns:
if not kb.data.cachedColumns and not conf.direct:
infoMsg = "fetching number of columns "
infoMsg += "for table '%s'" % conf.tbl
infoMsg += " on database '%s'" % conf.db
@@ -1298,8 +1298,10 @@ class Enumeration:
colList = conf.col.split(",")
kb.data.cachedColumns[conf.db] = {}
kb.data.cachedColumns[conf.db][conf.tbl] = {}
for column in colList:
kb.data.cachedColumns[conf.db][conf.tbl][column] = None
elif not kb.data.cachedColumns:
if kb.dbms == "MySQL" and not kb.data.has_information_schema:
errMsg = "information_schema not available, "
@@ -1359,7 +1361,7 @@ class Enumeration:
index += 1
if not kb.data.dumpedTable:
if not kb.data.dumpedTable and not conf.direct:
infoMsg = "fetching number of "
if conf.col:
infoMsg += "columns '%s' " % colString