Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1416cd0

Browse files
committed
Major enhancement to directly connect to the dbms without passing via a sql injection: adapted code accordingly - see #158. This feature relies on python third-party libraries to be able to connect to the database. For the moment it has been implemented for MySQL (with python-mysqldb module) and PostgreSQL (with python-psycopg2 module).
Minor layout adjustments.
1 parent 4ca1adb commit 1416cd0

32 files changed

Lines changed: 790 additions & 121 deletions

lib/controller/action.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def action():
4343

4444
# First of all we have to identify the back-end database management
4545
# system to be able to go ahead with the injection
46-
conf.dbmsHandler = setHandler()
46+
setHandler()
4747

4848
if not conf.dbmsHandler:
4949
htmlParsed = getHtmlErrorFp()
@@ -166,3 +166,6 @@ def action():
166166
# Miscellaneous options
167167
if conf.cleanup:
168168
conf.dbmsHandler.cleanup()
169+
170+
if conf.direct:
171+
conf.dbmsConnector.close()

lib/controller/controller.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def __selectInjection(injData):
7676
return "Quit"
7777

7878
else:
79-
warnMsg = "Invalid choice, retry"
79+
warnMsg = "invalid choice, retry"
8080
logger.warn(warnMsg)
8181
__selectInjection(injData)
8282

@@ -92,6 +92,13 @@ def start():
9292
if not conf.start:
9393
return
9494

95+
if conf.direct:
96+
initTargetEnv()
97+
setupTargetEnv()
98+
action()
99+
100+
return
101+
95102
if conf.url:
96103
kb.targetUrls.add(( conf.url, conf.method, conf.data, conf.cookie ))
97104

@@ -104,9 +111,9 @@ def start():
104111
infoMsg = "sqlmap got a total of %d targets" % len(kb.targetUrls)
105112
logger.info(infoMsg)
106113

107-
hostCount = 0
108-
cookieStr = ""
109-
setCookieAsInjectable = True
114+
hostCount = 0
115+
cookieStr = ""
116+
setCookieAsInjectable = True
110117

111118
for targetUrl, targetMethod, targetData, targetCookie in kb.targetUrls:
112119
try:

lib/controller/handler.py

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,19 @@
3434
from lib.core.settings import FIREBIRD_ALIASES
3535

3636
from plugins.dbms.mssqlserver import MSSQLServerMap
37+
from plugins.dbms.mssqlserver.connector import Connector as MSSQLServerConn
3738
from plugins.dbms.mysql import MySQLMap
39+
from plugins.dbms.mysql.connector import Connector as MySQLConn
3840
from plugins.dbms.oracle import OracleMap
41+
from plugins.dbms.oracle.connector import Connector as OracleConn
3942
from plugins.dbms.postgresql import PostgreSQLMap
43+
from plugins.dbms.postgresql.connector import Connector as PostgreSQLConn
4044
from plugins.dbms.sqlite import SQLiteMap
45+
from plugins.dbms.sqlite.connector import Connector as SQLiteConn
4146
from plugins.dbms.access import AccessMap
47+
from plugins.dbms.access.connector import Connector as AccessConn
4248
from plugins.dbms.firebird import FirebirdMap
49+
from plugins.dbms.firebird.connector import Connector as FirebirdConn
4350

4451
def setHandler():
4552
"""
@@ -50,16 +57,16 @@ def setHandler():
5057
count = 0
5158
dbmsNames = ( "MySQL", "Oracle", "PostgreSQL", "Microsoft SQL Server", "SQLite", "Microsoft Access", "Firebird" )
5259
dbmsMap = (
53-
( MYSQL_ALIASES, MySQLMap ),
54-
( ORACLE_ALIASES, OracleMap ),
55-
( PGSQL_ALIASES, PostgreSQLMap ),
56-
( MSSQL_ALIASES, MSSQLServerMap ),
57-
( SQLITE_ALIASES, SQLiteMap ),
58-
( ACCESS_ALIASES, AccessMap ),
59-
( FIREBIRD_ALIASES, FirebirdMap ),
60+
( MYSQL_ALIASES, MySQLMap, MySQLConn ),
61+
( ORACLE_ALIASES, OracleMap, OracleConn ),
62+
( PGSQL_ALIASES, PostgreSQLMap, PostgreSQLConn ),
63+
( MSSQL_ALIASES, MSSQLServerMap, MSSQLServerConn ),
64+
( SQLITE_ALIASES, SQLiteMap, SQLiteConn ),
65+
( ACCESS_ALIASES, AccessMap, AccessConn ),
66+
( FIREBIRD_ALIASES, FirebirdMap, FirebirdConn ),
6067
)
6168

62-
for dbmsAliases, dbmsEntry in dbmsMap:
69+
for dbmsAliases, dbmsMap, dbmsConn in dbmsMap:
6370
if conf.dbms and conf.dbms not in dbmsAliases:
6471
debugMsg = "skipping test for %s" % dbmsNames[count]
6572
logger.debug(debugMsg)
@@ -68,12 +75,15 @@ def setHandler():
6875

6976
continue
7077

71-
handler = dbmsEntry()
78+
handler = dbmsMap()
79+
conf.dbmsConnector = dbmsConn()
7280

7381
if handler.checkDbms():
7482
if not conf.dbms or conf.dbms in dbmsAliases:
7583
kb.dbmsDetected = True
7684

77-
return handler
85+
conf.dbmsHandler = handler
7886

79-
return None
87+
return
88+
else:
89+
conf.dbmsConnector = None

lib/core/agent.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,25 @@ def __init__(self):
4444
temp.start = randomStr(6)
4545
temp.stop = randomStr(6)
4646

47+
def payloadDirect(self, query):
48+
if query.startswith(" AND "):
49+
query = query.replace(" AND ", "SELECT ")
50+
elif query.startswith(" UNION ALL "):
51+
query = query.replace(" UNION ALL ", "")
52+
elif query.startswith("; "):
53+
query = query.replace("; ", "")
54+
55+
return query
56+
4757
def payload(self, place=None, parameter=None, value=None, newValue=None, negative=False, falseCond=False):
4858
"""
4959
This method replaces the affected parameter with the SQL
5060
injection statement to request
5161
"""
5262

63+
if conf.direct:
64+
return self.payloadDirect(newValue)
65+
5366
falseValue = ""
5467
negValue = ""
5568
retValue = ""
@@ -83,6 +96,9 @@ def payload(self, place=None, parameter=None, value=None, newValue=None, negativ
8396
return retValue
8497

8598
def fullPayload(self, query):
99+
if conf.direct:
100+
return self.payloadDirect(query)
101+
86102
query = self.prefixQuery(query)
87103
query = self.postfixQuery(query)
88104
payload = self.payload(newValue=query)
@@ -96,6 +112,9 @@ def prefixQuery(self, string):
96112
identified as valid
97113
"""
98114

115+
if conf.direct:
116+
return self.payloadDirect(string)
117+
99118
query = ""
100119

101120
if conf.prefix:
@@ -123,6 +142,9 @@ def postfixQuery(self, string, comment=None):
123142
SQL injection request
124143
"""
125144

145+
if conf.direct:
146+
return self.payloadDirect(string)
147+
126148
randInt = randomInt()
127149
randStr = randomStr()
128150

lib/core/common.py

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,21 @@
4747
from lib.core.convert import urlencode
4848
from lib.core.exception import sqlmapFilePathException
4949
from lib.core.exception import sqlmapNoneDataException
50+
from lib.core.exception import sqlmapMissingDependence
5051
from lib.core.exception import sqlmapSyntaxException
5152
from lib.core.settings import DESCRIPTION
5253
from lib.core.settings import IS_WIN
5354
from lib.core.settings import SITE
5455
from lib.core.settings import SQL_STATEMENTS
56+
from lib.core.settings import SUPPORTED_DBMS
5557
from lib.core.settings import VERSION_STRING
58+
from lib.core.settings import MSSQL_ALIASES
59+
from lib.core.settings import MYSQL_ALIASES
60+
from lib.core.settings import PGSQL_ALIASES
61+
from lib.core.settings import ORACLE_ALIASES
62+
from lib.core.settings import SQLITE_ALIASES
63+
from lib.core.settings import ACCESS_ALIASES
64+
from lib.core.settings import FIREBIRD_ALIASES
5665

5766
def paramToDict(place, parameters=None):
5867
"""
@@ -319,7 +328,7 @@ def getDirs(webApi=None):
319328
[directories.add(directory) for directory in defaultDirs]
320329

321330
return directories
322-
331+
323332
def filePathToString(filePath):
324333
strRepl = filePath.replace("/", "_").replace("\\", "_")
325334
strRepl = strRepl.replace(" ", "_").replace(":", "_")
@@ -329,18 +338,18 @@ def filePathToString(filePath):
329338
def dataToStdout(data):
330339
sys.stdout.write(data)
331340
sys.stdout.flush()
332-
341+
333342
def dataToSessionFile(data):
334343
if not conf.sessionFile:
335344
return
336345

337346
conf.sessionFP.write(data)
338347
conf.sessionFP.flush()
339-
348+
340349
def dataToDumpFile(dumpFile, data):
341350
dumpFile.write(data)
342351
dumpFile.flush()
343-
352+
344353
def dataToOutFile(data):
345354
if not data:
346355
return "No data retrieved"
@@ -586,10 +595,62 @@ def weAreFrozen():
586595

587596
return hasattr(sys, "frozen")
588597

598+
def parseTargetDirect():
599+
"""
600+
Parse target dbms and set some attributes into the configuration singleton.
601+
"""
602+
603+
if not conf.direct:
604+
return
605+
606+
details = None
607+
608+
for dbms in SUPPORTED_DBMS:
609+
details = re.search("^(%s)://(.+?)\:(.+?)\@(.+?)\:([\d]+)\/(.+?)$" % dbms, conf.direct, re.I)
610+
611+
if details:
612+
conf.dbms = details.group(1)
613+
conf.dbmsUser = details.group(2)
614+
conf.dbmsPass = details.group(3)
615+
conf.hostname = details.group(4)
616+
conf.port = int(details.group(5))
617+
conf.dbmsDb = details.group(6)
618+
619+
conf.parameters[None] = "direct connection"
620+
621+
break
622+
623+
if not details:
624+
errMsg = "invalid target details, valid syntax is for instance: mysql://USER:PASSWORD@DBMS_IP:DBMS_PORT/DATABASE_NAME"
625+
raise sqlmapSyntaxException, errMsg
626+
627+
# TODO: add details for others python DBMS libraries
628+
dbmsDict = { "Microsoft SQL Server": [MSSQL_ALIASES, "python-pymssql", "http://pymssql.sourceforge.net/"],
629+
"MySQL": [MYSQL_ALIASES, "python-mysqldb", "http://mysql-python.sourceforge.net/"],
630+
"PostgreSQL": [PGSQL_ALIASES, "python-psycopg2", "http://initd.org/psycopg/"],
631+
"Oracle": [ORACLE_ALIASES, "", ""],
632+
"SQLite": [SQLITE_ALIASES, "", ""],
633+
"Access": [ACCESS_ALIASES, "", ""],
634+
"Firebird": [FIREBIRD_ALIASES, "", ""] }
635+
636+
for dbmsName, data in dbmsDict.items():
637+
if conf.dbms in data[0]:
638+
try:
639+
if dbmsName == "Microsoft SQL Server":
640+
import pymssql
641+
elif dbmsName == "MySQL":
642+
import MySQLdb
643+
elif dbmsName == "PostgreSQL":
644+
import psycopg2
645+
except ImportError, _:
646+
errMsg = "sqlmap requires %s third-party library " % data[1]
647+
errMsg += "in order to directly connect to the database "
648+
errMsg += "%s. Download from %s" % (dbmsName, data[2])
649+
raise sqlmapMissingDependence, errMsg
650+
589651
def parseTargetUrl():
590652
"""
591-
Parse target url and set some attributes into the configuration
592-
singleton.
653+
Parse target url and set some attributes into the configuration singleton.
593654
"""
594655

595656
if not conf.url:

lib/core/convert.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
import struct
3333
import urllib
3434

35+
from lib.core.data import conf
36+
3537
def base64decode(string):
3638
return string.decode("base64")
3739

@@ -77,6 +79,9 @@ def urldecode(string):
7779
return result
7880

7981
def urlencode(string, safe=":/?%&=", convall=False):
82+
if conf.direct:
83+
return string
84+
8085
result = None
8186

8287
if string is None:

lib/core/dump.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def lister(self, header, elements, sort=True):
9292
if isinstance(element, str):
9393
self.__write("[*] %s" % element)
9494
elif isinstance(element, (list, tuple, set)):
95-
self.__write("[*] " + ", ".join(e for e in element))
95+
self.__write("[*] " + ", ".join(str(e) for e in element))
9696

9797
if elements:
9898
self.__write("")
@@ -321,11 +321,11 @@ def dbTableValues(self, tableValues):
321321
info = tableValues[column]
322322
value = info["values"][i]
323323

324-
if re.search("^[\ *]*$", value):
324+
if re.search("^[\ *]*$", str(value)):
325325
value = "NULL"
326326

327327
maxlength = int(info["length"])
328-
blank = " " * (maxlength - len(value))
328+
blank = " " * (maxlength - len(str(value)))
329329
self.__write("| %s%s" % (value, blank), n=False)
330330

331331
if not conf.multipleTargets and field == fields:

0 commit comments

Comments
 (0)