diff --git a/extra/shutils/regressiontest.py b/extra/shutils/regressiontest.py
index 24b62a801..52c5d1b81 100644
--- a/extra/shutils/regressiontest.py
+++ b/extra/shutils/regressiontest.py
@@ -26,7 +26,7 @@ SMTP_SERVER = "127.0.0.1"
SMTP_PORT = 25
SMTP_TIMEOUT = 30
FROM = "regressiontest@sqlmap.org"
-TO = "dev@sqlmap.org"
+TO = ["bernardo.damele@gmail.com", "miroslav.stampar@gmail.com"]
SUBJECT = "Regression test results on %s using revision %s" % (TIME, REVISION)
def prepare_email(content):
@@ -87,18 +87,23 @@ def main():
test_counts.append(test_count)
- console_output_fd = codecs.open(os.path.join(output_folder, "console_output"), "rb", "utf8")
- console_output = console_output_fd.read()
- console_output_fd.close()
+ console_output_file = os.path.join(output_folder, "console_output")
+ log_file = os.path.join(output_folder, "debiandev", "log")
+ traceback_file = os.path.join(output_folder, "traceback")
- attachments[test_count] = str(console_output)
+ if os.path.exists(console_output_file):
+ console_output_fd = codecs.open(console_output_file, "rb", "utf8")
+ console_output = console_output_fd.read()
+ console_output_fd.close()
+ attachments[test_count] = str(console_output)
- log_fd = codecs.open(os.path.join(output_folder, "debiandev", "log"), "rb", "utf8")
- log = log_fd.read()
- log_fd.close()
+ if os.path.exists(log_file):
+ log_fd = codecs.open(log_file, "rb", "utf8")
+ log = log_fd.read()
+ log_fd.close()
- if traceback:
- traceback_fd = codecs.open(os.path.join(output_folder, "traceback"), "rb", "utf8")
+ if os.path.exists(traceback_file):
+ traceback_fd = codecs.open(traceback_file, "rb", "utf8")
traceback = traceback_fd.read()
traceback_fd.close()
diff --git a/extra/shutils/regressiontest_cronjob.sh b/extra/shutils/regressiontest_cronjob.sh
index 9a20bd3d6..b569b4e8d 100755
--- a/extra/shutils/regressiontest_cronjob.sh
+++ b/extra/shutils/regressiontest_cronjob.sh
@@ -6,9 +6,18 @@
SQLMAP_HOME="/opt/sqlmap"
REGRESSION_SCRIPT="${SQLMAP_HOME}/extra/shutils"
+FROM="regressiontest@sqlmap.org"
+TO="bernardo.damele@gmail.com, miroslav.stampar@gmail.com"
+SUBJECT="Automated regression test failed on $(date)"
+
cd $SQLMAP_HOME
git pull
rm -f output 2>/dev/null
cd $REGRESSION_SCRIPT
-python regressiontest.py
+python regressiontest.py 1>/tmp/regressiontest.log 2>&1
+
+if [ $? -ne 0 ]
+then
+ cat /tmp/regressiontest.log | mailx -s "${SUBJECT}" -aFrom:${FROM} ${TO}
+fi
diff --git a/lib/core/agent.py b/lib/core/agent.py
index d81d4be83..0e0770b01 100644
--- a/lib/core/agent.py
+++ b/lib/core/agent.py
@@ -535,7 +535,7 @@ class Agent(object):
elif fieldsNoSelect:
concatenatedQuery = "CONCAT('%s',%s,'%s')" % (kb.chars.start, concatenatedQuery, kb.chars.stop)
- elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.ORACLE, DBMS.SQLITE, DBMS.DB2):
+ elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.ORACLE, DBMS.SQLITE, DBMS.DB2, DBMS.FIREBIRD):
if fieldsExists:
concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.chars.start, 1)
concatenatedQuery += "||'%s'" % kb.chars.stop
@@ -822,8 +822,7 @@ class Agent(object):
limitedQuery += " %s" % limitStr
elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2):
- if " ORDER BY " in limitedQuery and "(SELECT " in limitedQuery:
- orderBy = limitedQuery[limitedQuery.index(" ORDER BY "):]
+ if " ORDER BY " in limitedQuery and "SELECT " in limitedQuery:
limitedQuery = limitedQuery[:limitedQuery.index(" ORDER BY ")]
if query.startswith("SELECT "):
@@ -831,6 +830,7 @@ class Agent(object):
limitedQuery = "%s FROM (%s,%s" % (untilFrom, untilFrom.replace(delimiter, ','), limitStr)
else:
limitedQuery = "%s FROM (SELECT %s,%s" % (untilFrom, ','.join(f for f in field), limitStr)
+
limitedQuery = limitedQuery % fromFrom
limitedQuery += "=%d" % (num + 1)
diff --git a/lib/core/dump.py b/lib/core/dump.py
index b7b98818e..4b553f424 100644
--- a/lib/core/dump.py
+++ b/lib/core/dump.py
@@ -175,7 +175,8 @@ class Dump(object):
for setting in settings:
self._write(" %s: %s" % (subHeader, setting))
- self.singleString("")
+ if userSettings:
+ self.singleString("")
def dbs(self, dbs):
self.lister("available databases", dbs)
diff --git a/lib/core/testing.py b/lib/core/testing.py
index 376747012..ff4ed5258 100644
--- a/lib/core/testing.py
+++ b/lib/core/testing.py
@@ -167,6 +167,9 @@ def liveTest():
result = runCase(switches, parse)
+ test_case_fd = codecs.open(os.path.join(paths.SQLMAP_OUTPUT_PATH, "test_case"), "wb", UNICODE_ENCODING)
+ test_case_fd.write("%s\n" % name)
+
if result:
logger.info("test passed")
cleanCase()
@@ -183,6 +186,7 @@ def liveTest():
errMsg += " - SQL injection not detected"
logger.error(errMsg)
+ test_case_fd.write("%s\n" % errMsg)
if failedParseOn:
console_output_fd = codecs.open(os.path.join(paths.SQLMAP_OUTPUT_PATH, "console_output"), "wb", UNICODE_ENCODING)
@@ -199,6 +203,7 @@ def liveTest():
if conf.stopFail is True:
return retVal
+ test_case_fd.close()
retVal &= bool(result)
dataToStdout("\n")
diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py
index d2edbb1ec..e84f0680a 100644
--- a/lib/techniques/error/use.py
+++ b/lib/techniques/error/use.py
@@ -23,6 +23,7 @@ from lib.core.common import incrementCounter
from lib.core.common import initTechnique
from lib.core.common import isListLike
from lib.core.common import isNumPosStrValue
+from lib.core.common import isTechniqueAvailable
from lib.core.common import listToStrValue
from lib.core.common import readInput
from lib.core.common import unArrayizeValue
@@ -34,6 +35,7 @@ from lib.core.data import logger
from lib.core.data import queries
from lib.core.dicts import FROM_DUMMY_TABLE
from lib.core.enums import DBMS
+from lib.core.enums import PAYLOAD
from lib.core.settings import CHECK_ZERO_COLUMNS_THRESHOLD
from lib.core.settings import MYSQL_ERROR_CHUNK_LENGTH
from lib.core.settings import MSSQL_ERROR_CHUNK_LENGTH
@@ -180,6 +182,9 @@ def _errorFields(expression, expressionFields, expressionFieldsList, num=None, e
else:
expressionReplaced = expression.replace(expressionFields, field, 1)
+ if kb.technique == PAYLOAD.TECHNIQUE.QUERY and Backend.isDbms(DBMS.FIREBIRD) and expressionReplaced.startswith("SELECT "):
+ expressionReplaced = "SELECT %s" % agent.concatQuery(expressionReplaced)
+
output = NULL if emptyFields and field in emptyFields else _oneShotErrorUse(expressionReplaced, field)
if not kb.threadContinue:
diff --git a/plugins/dbms/firebird/fingerprint.py b/plugins/dbms/firebird/fingerprint.py
index cf005bd9d..8d192d3cb 100644
--- a/plugins/dbms/firebird/fingerprint.py
+++ b/plugins/dbms/firebird/fingerprint.py
@@ -74,6 +74,7 @@ class Fingerprint(GenericFingerprint):
("1.5", ("NULLIF(%d,%d) IS NULL", "EXISTS(SELECT CURRENT_TRANSACTION FROM RDB$DATABASE)")),
("2.0", ("EXISTS(SELECT CURRENT_TIME(0) FROM RDB$DATABASE)", "BIT_LENGTH(%d)>0", "CHAR_LENGTH(%d)>0")),
("2.1", ("BIN_XOR(%d,%d)=0", "PI()>0.%d", "RAND()<1.%d", "FLOOR(1.%d)>=0")),
+ # TODO: add test for Firebird 2.5
)
for i in xrange(len(table)):
@@ -122,7 +123,7 @@ class Fingerprint(GenericFingerprint):
logger.info(infoMsg)
randInt = randomInt()
- result = inject.checkBooleanExpression("EXISTS(SELECT * FROM RDB$DATABASE WHERE %d=%d)" % (randInt, randInt))
+ result = inject.checkBooleanExpression("(SELECT COUNT(*) FROM RDB$DATABASE WHERE %d=%d)>0" % (randInt, randInt))
if result:
infoMsg = "confirming %s" % DBMS.FIREBIRD
diff --git a/plugins/dbms/firebird/syntax.py b/plugins/dbms/firebird/syntax.py
index f0d91c78f..a77359249 100644
--- a/plugins/dbms/firebird/syntax.py
+++ b/plugins/dbms/firebird/syntax.py
@@ -16,6 +16,9 @@ class Syntax(GenericSyntax):
@staticmethod
def escape(expression, quote=True):
if isDBMSVersionAtLeast('2.1'):
+ if expression == u"'''":
+ return "ASCII_CHAR(%d)" % (ord("'"))
+
if quote:
while True:
index = expression.find("'")
diff --git a/plugins/dbms/mssqlserver/connector.py b/plugins/dbms/mssqlserver/connector.py
index c859f9632..7eb0a82bb 100644
--- a/plugins/dbms/mssqlserver/connector.py
+++ b/plugins/dbms/mssqlserver/connector.py
@@ -41,7 +41,7 @@ class Connector(GenericConnector):
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:
+ except (pymssql.InterfaceError, pymssql.OperationalError), msg:
raise SqlmapConnectionException(msg)
self.initCursor()
diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py
index 03d3e8256..0dbb6d6f8 100644
--- a/plugins/generic/databases.py
+++ b/plugins/generic/databases.py
@@ -513,22 +513,24 @@ class Databases:
query = rootQuery.inband.query % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db))
query += condQuery
elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2):
- query = rootQuery.inband.query % unsafeSQLIdentificatorNaming(tbl.upper())
+ query = rootQuery.inband.query % (unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper()))
query += condQuery
elif Backend.isDbms(DBMS.MSSQL):
query = rootQuery.inband.query % (conf.db, conf.db, conf.db, conf.db,
conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl).split(".")[-1])
query += condQuery.replace("[DB]", conf.db)
- elif Backend.isDbms(DBMS.SQLITE):
+ elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD):
query = rootQuery.inband.query % tbl
values = inject.getValue(query, blind=False, time=False)
if Backend.isDbms(DBMS.MSSQL) and isNoneValue(values):
index, values = 1, []
+
while True:
query = rootQuery.inband.query2 % (conf.db, tbl, index)
value = unArrayizeValue(inject.getValue(query, blind=False, time=False))
+
if isNoneValue(value) or value == " ":
break
else:
@@ -591,7 +593,7 @@ class Databases:
query += condQuery
elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2):
- query = rootQuery.blind.count % unsafeSQLIdentificatorNaming(tbl.upper())
+ query = rootQuery.blind.count % (unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper()))
query += condQuery
elif Backend.isDbms(DBMS.MSSQL):
@@ -639,7 +641,7 @@ class Databases:
query += condQuery
field = None
elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2):
- query = rootQuery.blind.query % unsafeSQLIdentificatorNaming(tbl.upper())
+ query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper()))
query += condQuery
field = None
elif Backend.isDbms(DBMS.MSSQL):
@@ -659,7 +661,7 @@ class Databases:
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL):
query = rootQuery.blind.query2 % (unsafeSQLIdentificatorNaming(tbl), column, unsafeSQLIdentificatorNaming(conf.db))
elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2):
- query = rootQuery.blind.query2 % (unsafeSQLIdentificatorNaming(tbl.upper()), column)
+ query = rootQuery.blind.query2 % (unsafeSQLIdentificatorNaming(tbl.upper()), column, unsafeSQLIdentificatorNaming(conf.db.upper()))
elif Backend.isDbms(DBMS.MSSQL):
query = rootQuery.blind.query2 % (conf.db, conf.db, conf.db, conf.db, column, conf.db,
conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl).split(".")[-1])
@@ -736,7 +738,11 @@ class Databases:
db = db.upper()
table = table.upper()
- query = "SELECT %s FROM %s.%s" % (queries[Backend.getIdentifiedDbms()].count.query % '*', safeSQLIdentificatorNaming(db), safeSQLIdentificatorNaming(table, True))
+ if Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD):
+ query = "SELECT %s FROM %s" % (queries[Backend.getIdentifiedDbms()].count.query % '*', safeSQLIdentificatorNaming(table, True))
+ else:
+ query = "SELECT %s FROM %s.%s" % (queries[Backend.getIdentifiedDbms()].count.query % '*', safeSQLIdentificatorNaming(db), safeSQLIdentificatorNaming(table, True))
+
count = inject.getValue(query, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS)
if isNumPosStrValue(count):
@@ -759,7 +765,7 @@ class Databases:
if not conf.db:
conf.db, conf.tbl = conf.tbl.split(".")
- if conf.tbl is not None and conf.db is None:
+ if conf.tbl is not None and conf.db is None and Backend.getIdentifiedDbms() not in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD):
warnMsg = "missing database parameter. sqlmap is going to "
warnMsg += "use the current database to retrieve the "
warnMsg += "number of entries for table '%s'" % unsafeSQLIdentificatorNaming(conf.tbl)
diff --git a/plugins/generic/search.py b/plugins/generic/search.py
index 6917b10da..238ec8471 100644
--- a/plugins/generic/search.py
+++ b/plugins/generic/search.py
@@ -29,6 +29,7 @@ from lib.core.enums import PAYLOAD
from lib.core.exception import SqlmapMissingMandatoryOptionException
from lib.core.exception import SqlmapUserQuitException
from lib.core.settings import CURRENT_DB
+from lib.core.settings import METADB_SUFFIX
from lib.request import inject
from lib.techniques.brute.use import columnExists
from lib.techniques.brute.use import tableExists
@@ -199,7 +200,7 @@ class Search:
if isinstance(values, basestring):
values = [values]
for value in values:
- newValues.append(["SQLite_masterdb", value])
+ newValues.append(["SQLite_%s" % METADB_SUFFIX, value])
values = newValues
@@ -258,7 +259,7 @@ class Search:
if tblConsider == "2":
continue
else:
- foundTbls["SQLite_masterdb"] = []
+ foundTbls["SQLite_%s" % METADB_SUFFIX] = []
for db in foundTbls.keys():
db = safeSQLIdentificatorNaming(db)
diff --git a/xml/livetests.xml b/xml/livetests.xml
index 841d2f174..6d74eec1b 100644
--- a/xml/livetests.xml
+++ b/xml/livetests.xml
@@ -888,6 +888,217 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1044,6 +1255,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1671,6 +1897,294 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1845,6 +2359,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1886,7 +2467,6 @@
-
diff --git a/xml/payloads.xml b/xml/payloads.xml
index d9cb48b22..63245157c 100644
--- a/xml/payloads.xml
+++ b/xml/payloads.xml
@@ -1996,6 +1996,24 @@ Formats:
SQLite
+
+ Firebird inline queries
+ 6
+ 2
+ 1
+ 1,2,3,8
+ 3
+ [QUERY]
+
+ SELECT '[DELIMITER_START]'||(CASE [RANDNUM] WHEN [RANDNUM] THEN 1 ELSE 0 END)||'[DELIMITER_STOP]' FROM RDB$DATABASE
+
+
+ [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP]
+
+
+ Firebird
+
+
diff --git a/xml/queries.xml b/xml/queries.xml
index dce39fa72..254242824 100644
--- a/xml/queries.xml
+++ b/xml/queries.xml
@@ -209,7 +209,7 @@
-
+
@@ -269,8 +269,8 @@
-
-
+
+
@@ -359,12 +359,12 @@
+
-
@@ -401,16 +401,16 @@
+
+
-
-
@@ -603,8 +603,8 @@
-
-
+
+