Merge branch 'master' of github.com:sqlmapproject/sqlmap

This commit is contained in:
Miroslav Stampar 2013-01-10 12:09:48 +01:00
commit 05705857a9
9 changed files with 307 additions and 92 deletions

View File

@ -3309,3 +3309,21 @@ def isNumber(value):
return False return False
else: else:
return True return True
def pollProcess(process, suppress_errors=False):
while True:
dataToStdout(".")
time.sleep(1)
returncode = process.poll()
if returncode is not None:
if not suppress_errors:
if returncode == 0:
dataToStdout(" done\n")
elif returncode < 0:
dataToStdout(" process terminated by signal %d\n" % returncode)
elif returncode > 0:
dataToStdout(" quit unexpectedly with return code %d\n" % returncode)
break

View File

@ -11,6 +11,7 @@ except:
import md5 import md5
import sha import sha
import json
import pickle import pickle
import sys import sys
import struct import struct
@ -126,3 +127,6 @@ def stdoutencode(data):
retVal = data.encode(UNICODE_ENCODING) retVal = data.encode(UNICODE_ENCODING)
return retVal return retVal
def jsonize(data):
return json.dumps(data, sort_keys=False, indent=4)

View File

@ -33,6 +33,3 @@ FORMATTER = logging.Formatter("\r[%(asctime)s] [%(levelname)s] %(message)s", "%H
LOGGER_HANDLER.setFormatter(FORMATTER) LOGGER_HANDLER.setFormatter(FORMATTER)
LOGGER.addHandler(LOGGER_HANDLER) LOGGER.addHandler(LOGGER_HANDLER)
LOGGER.setLevel(logging.WARN) LOGGER.setLevel(logging.WARN)
# to handle logger with the RESTful API
LOGGER_OUTPUT = StringIO.StringIO()

View File

@ -52,7 +52,9 @@ from lib.core.common import singleTimeWarnMessage
from lib.core.common import UnicodeRawConfigParser from lib.core.common import UnicodeRawConfigParser
from lib.core.common import urldecode from lib.core.common import urldecode
from lib.core.common import urlencode from lib.core.common import urlencode
from lib.core.convert import base64pickle
from lib.core.convert import base64unpickle from lib.core.convert import base64unpickle
from lib.core.convert import jsonize
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
@ -1804,6 +1806,33 @@ def _mergeOptions(inputOptions, overrideOptions):
if hasattr(conf, key) and conf[key] is None: if hasattr(conf, key) and conf[key] is None:
conf[key] = value conf[key] = value
# Logger recorder object, which keeps the log structure
class LogRecorder(logging.StreamHandler):
"""
Logging handler class which only records CUSTOM_LOGGING.PAYLOAD entries
to a global list.
"""
loghist = []
def emit(self, record):
"""
Simply record the emitted events.
"""
self.loghist.append({'levelname': record.levelname,
'text': record.msg % record.args if record.args else record.msg,
'id': len(self.loghist)+1})
if conf.fdLog:
# TODO: this is very heavy operation and slows down a lot the
# whole execution of the sqlmap engine, find an alternative
os.write(conf.fdLog, base64pickle(self.loghist))
def _setRestAPILog():
if hasattr(conf, "fdLog") and conf.fdLog:
logger.removeHandler(LOGGER_HANDLER)
LOGGER_RECORDER = LogRecorder()
logger.addHandler(LOGGER_RECORDER)
def _setTrafficOutputFP(): def _setTrafficOutputFP():
if conf.trafficFile: if conf.trafficFile:
infoMsg = "setting file for logging HTTP traffic" infoMsg = "setting file for logging HTTP traffic"
@ -2069,14 +2098,13 @@ def init(inputOptions=AttribDict(), overrideOptions=False):
if not inputOptions.disableColoring: if not inputOptions.disableColoring:
coloramainit() coloramainit()
elif hasattr(LOGGER_HANDLER, "disable_coloring"):
LOGGER_HANDLER.disable_coloring = True
_setConfAttributes() _setConfAttributes()
_setKnowledgeBaseAttributes() _setKnowledgeBaseAttributes()
_mergeOptions(inputOptions, overrideOptions) _mergeOptions(inputOptions, overrideOptions)
_useWizardInterface() _useWizardInterface()
setVerbosity() setVerbosity()
_setRestAPILog()
_saveCmdline() _saveCmdline()
_setRequestFromFile() _setRequestFromFile()
_cleanupOptions() _cleanupOptions()

View File

@ -7,13 +7,22 @@ See the file 'doc/COPYING' for copying permission
import errno import errno
import os import os
import subprocess
import sys import sys
import time import time
from lib.core.common import dataToStdout from lib.core.common import dataToStdout
from lib.core.settings import IS_WIN from lib.core.settings import IS_WIN
if not IS_WIN: if IS_WIN:
try:
from win32file import ReadFile, WriteFile
from win32pipe import PeekNamedPipe
except ImportError:
pass
import msvcrt
else:
import select
import fcntl import fcntl
if (sys.hexversion >> 16) >= 0x202: if (sys.hexversion >> 16) >= 0x202:
@ -61,30 +70,132 @@ def blockingWriteToFD(fd, data):
break break
def setNonBlocking(fd): # the following code is taken from http://code.activestate.com/recipes/440554-module-to-allow-asynchronous-subprocess-use-on-win/
""" class Popen(subprocess.Popen):
Make a file descriptor non-blocking def recv(self, maxsize=None):
""" return self._recv('stdout', maxsize)
if IS_WIN is not True: def recv_err(self, maxsize=None):
flags = fcntl.fcntl(fd, FCNTL.F_GETFL) return self._recv('stderr', maxsize)
flags = flags | os.O_NONBLOCK
fcntl.fcntl(fd, FCNTL.F_SETFL, flags)
def pollProcess(process, suppress_errors=False): def send_recv(self, input='', maxsize=None):
while True: return self.send(input), self.recv(maxsize), self.recv_err(maxsize)
dataToStdout(".")
time.sleep(1)
returncode = process.poll() def get_conn_maxsize(self, which, maxsize):
if maxsize is None:
maxsize = 1024
elif maxsize < 1:
maxsize = 1
return getattr(self, which), maxsize
if returncode is not None: def _close(self, which):
if not suppress_errors: getattr(self, which).close()
if returncode == 0: setattr(self, which, None)
dataToStdout(" done\n")
elif returncode < 0:
dataToStdout(" process terminated by signal %d\n" % returncode)
elif returncode > 0:
dataToStdout(" quit unexpectedly with return code %d\n" % returncode)
if subprocess.mswindows:
def send(self, input):
if not self.stdin:
return None
try:
x = msvcrt.get_osfhandle(self.stdin.fileno())
(errCode, written) = WriteFile(x, input)
except ValueError:
return self._close('stdin')
except (subprocess.pywintypes.error, Exception), why:
if why[0] in (109, errno.ESHUTDOWN):
return self._close('stdin')
raise
return written
def _recv(self, which, maxsize):
conn, maxsize = self.get_conn_maxsize(which, maxsize)
if conn is None:
return None
try:
x = msvcrt.get_osfhandle(conn.fileno())
(read, nAvail, nMessage) = PeekNamedPipe(x, 0)
if maxsize < nAvail:
nAvail = maxsize
if nAvail > 0:
(errCode, read) = ReadFile(x, nAvail, None)
except ValueError:
return self._close(which)
except (subprocess.pywintypes.error, Exception), why:
if why[0] in (109, errno.ESHUTDOWN):
return self._close(which)
raise
if self.universal_newlines:
read = self._translate_newlines(read)
return read
else:
def send(self, input):
if not self.stdin:
return None
if not select.select([], [self.stdin], [], 0)[1]:
return 0
try:
written = os.write(self.stdin.fileno(), input)
except OSError, why:
if why[0] == errno.EPIPE: #broken pipe
return self._close('stdin')
raise
return written
def _recv(self, which, maxsize):
conn, maxsize = self.get_conn_maxsize(which, maxsize)
if conn is None:
return None
flags = fcntl.fcntl(conn, fcntl.F_GETFL)
if not conn.closed:
fcntl.fcntl(conn, fcntl.F_SETFL, flags| os.O_NONBLOCK)
try:
if not select.select([conn], [], [], 0)[0]:
return ''
r = conn.read(maxsize)
if not r:
return self._close(which)
if self.universal_newlines:
r = self._translate_newlines(r)
return r
finally:
if not conn.closed:
fcntl.fcntl(conn, fcntl.F_SETFL, flags)
def recv_some(p, t=.1, e=1, tr=5, stderr=0):
if tr < 1:
tr = 1
x = time.time()+t
y = []
r = ''
if stderr:
pr = p.recv_err
else:
pr = p.recv
while time.time() < x or r:
r = pr()
if r is None:
break break
elif r:
y.append(r)
else:
time.sleep(max((x-time.time())/tr, 0))
return ''.join(y)
def send_all(p, data):
if not data:
return
while len(data):
sent = p.send(data)
data = buffer(data, sent)

View File

@ -13,13 +13,13 @@ from subprocess import PIPE
from subprocess import Popen as execute from subprocess import Popen as execute
from lib.core.common import dataToStdout from lib.core.common import dataToStdout
from lib.core.common import pollProcess
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.data import paths from lib.core.data import paths
from lib.core.revision import getRevisionNumber from lib.core.revision import getRevisionNumber
from lib.core.settings import GIT_REPOSITORY from lib.core.settings import GIT_REPOSITORY
from lib.core.settings import IS_WIN from lib.core.settings import IS_WIN
from lib.core.subprocessng import pollProcess
def update(): def update():
if not conf.updateAll: if not conf.updateAll:

View File

@ -664,7 +664,7 @@ def cmdLineParser():
help="Simple wizard interface for beginner users") help="Simple wizard interface for beginner users")
# Hidden and/or experimental options # Hidden and/or experimental options
parser.add_option("--pickle", dest="pickledOptions", help=SUPPRESS_HELP) parser.add_option("--pickled-options", dest="pickledOptions", help=SUPPRESS_HELP)
parser.add_option("--profile", dest="profile", action="store_true", parser.add_option("--profile", dest="profile", action="store_true",
help=SUPPRESS_HELP) help=SUPPRESS_HELP)

View File

@ -10,9 +10,7 @@ import re
import sys import sys
import time import time
from select import select
from subprocess import PIPE from subprocess import PIPE
from subprocess import Popen as execute
from lib.core.common import dataToStdout from lib.core.common import dataToStdout
from lib.core.common import Backend from lib.core.common import Backend
@ -21,6 +19,7 @@ from lib.core.common import getRemoteIP
from lib.core.common import getUnicode from lib.core.common import getUnicode
from lib.core.common import normalizePath from lib.core.common import normalizePath
from lib.core.common import ntToPosixSlashes from lib.core.common import ntToPosixSlashes
from lib.core.common import pollProcess
from lib.core.common import randomRange from lib.core.common import randomRange
from lib.core.common import randomStr from lib.core.common import randomStr
from lib.core.common import readInput from lib.core.common import readInput
@ -35,9 +34,14 @@ from lib.core.settings import IS_WIN
from lib.core.settings import UNICODE_ENCODING from lib.core.settings import UNICODE_ENCODING
from lib.core.subprocessng import blockingReadFromFD from lib.core.subprocessng import blockingReadFromFD
from lib.core.subprocessng import blockingWriteToFD from lib.core.subprocessng import blockingWriteToFD
from lib.core.subprocessng import pollProcess from lib.core.subprocessng import Popen as execute
from lib.core.subprocessng import setNonBlocking from lib.core.subprocessng import send_all
from lib.core.subprocessng import recv_some
if IS_WIN:
import msvcrt
else:
from select import select
class Metasploit: class Metasploit:
""" """
@ -410,14 +414,14 @@ class Metasploit:
if not Backend.isOs(OS.WINDOWS): if not Backend.isOs(OS.WINDOWS):
return return
proc.stdin.write("use espia\n") send_all(proc, "use espia\n")
proc.stdin.write("use incognito\n") send_all(proc, "use incognito\n")
# This extension is loaded by default since Metasploit > 3.7 # This extension is loaded by default since Metasploit > 3.7
#proc.stdin.write("use priv\n") #send_all(proc, "use priv\n")
# This extension freezes the connection on 64-bit systems # This extension freezes the connection on 64-bit systems
#proc.stdin.write("use sniffer\n") #send_all(proc, "use sniffer\n")
proc.stdin.write("sysinfo\n") send_all(proc, "sysinfo\n")
proc.stdin.write("getuid\n") send_all(proc, "getuid\n")
if conf.privEsc: if conf.privEsc:
print print
@ -427,7 +431,7 @@ class Metasploit:
infoMsg += "techniques, including kitrap0d" infoMsg += "techniques, including kitrap0d"
logger.info(infoMsg) logger.info(infoMsg)
proc.stdin.write("getsystem\n") send_all(proc, "getsystem\n")
infoMsg = "displaying the list of Access Tokens availables. " infoMsg = "displaying the list of Access Tokens availables. "
infoMsg += "Choose which user you want to impersonate by " infoMsg += "Choose which user you want to impersonate by "
@ -435,15 +439,11 @@ class Metasploit:
infoMsg += "'getsystem' does not success to elevate privileges" infoMsg += "'getsystem' does not success to elevate privileges"
logger.info(infoMsg) logger.info(infoMsg)
proc.stdin.write("list_tokens -u\n") send_all(proc, "list_tokens -u\n")
proc.stdin.write("getuid\n") send_all(proc, "getuid\n")
def _controlMsfCmd(self, proc, func): def _controlMsfCmd(self, proc, func):
stdin_fd = sys.stdin.fileno() stdin_fd = sys.stdin.fileno()
setNonBlocking(stdin_fd)
proc_out_fd = proc.stdout.fileno()
setNonBlocking(proc_out_fd)
while True: while True:
returncode = proc.poll() returncode = proc.poll()
@ -456,39 +456,63 @@ class Metasploit:
return returncode return returncode
try: try:
ready_fds = select([stdin_fd, proc_out_fd], [], [], 1) if IS_WIN:
timeout = 3
if stdin_fd in ready_fds[0]: inp = ""
try: start_time = time.time()
proc.stdin.write(blockingReadFromFD(stdin_fd))
except IOError:
# Probably the child has exited
pass
if proc_out_fd in ready_fds[0]: while True:
out = blockingReadFromFD(proc_out_fd) if msvcrt.kbhit():
blockingWriteToFD(sys.stdout.fileno(), out) char = msvcrt.getche()
# For --os-pwn and --os-bof if ord(char) == 13: # enter_key
pwnBofCond = self.connectionStr.startswith("reverse") break
pwnBofCond &= "Starting the payload handler" in out elif ord(char) >= 32: # space_char
inp += char
# For --os-smbrelay if len(inp) == 0 and (time.time() - start_time) > timeout:
smbRelayCond = "Server started" in out break
if pwnBofCond or smbRelayCond: if len(inp) > 0:
func() try:
send_all(proc, inp)
except IOError:
# Probably the child has exited
pass
else:
ready_fds = select([stdin_fd], [], [], 1)
if "Starting the payload handler" in out and "shell" in self.payloadStr: if stdin_fd in ready_fds[0]:
if Backend.isOs(OS.WINDOWS): try:
proc.stdin.write("whoami\n") send_all(proc, blockingReadFromFD(stdin_fd))
else: except IOError:
proc.stdin.write("uname -a ; id\n") # Probably the child has exited
pass
metSess = re.search("Meterpreter session ([\d]+) opened", out) out = recv_some(proc, t=.1, e=0)
blockingWriteToFD(sys.stdout.fileno(), out)
if metSess: # For --os-pwn and --os-bof
self._loadMetExtensions(proc, metSess.group(1)) pwnBofCond = self.connectionStr.startswith("reverse")
pwnBofCond &= "Starting the payload handler" in out
# For --os-smbrelay
smbRelayCond = "Server started" in out
if pwnBofCond or smbRelayCond:
func()
if "Starting the payload handler" in out and "shell" in self.payloadStr:
if Backend.isOs(OS.WINDOWS):
send_all(proc, "whoami\n")
else:
send_all(proc, "uname -a ; id\n")
metSess = re.search("Meterpreter session ([\d]+) opened", out)
if metSess:
self._loadMetExtensions(proc, metSess.group(1))
except EOFError: except EOFError:
returncode = proc.wait() returncode = proc.wait()

View File

@ -14,25 +14,26 @@ import tempfile
import types import types
from subprocess import PIPE from subprocess import PIPE
from subprocess import Popen
from lib.controller.controller import start from lib.controller.controller import start
from lib.core.common import unArrayizeValue from lib.core.common import unArrayizeValue
from lib.core.convert import base64pickle from lib.core.convert import base64pickle
from lib.core.convert import base64unpickle
from lib.core.convert import hexencode from lib.core.convert import hexencode
from lib.core.convert import jsonize
from lib.core.convert import stdoutencode from lib.core.convert import stdoutencode
from lib.core.data import paths from lib.core.data import paths
from lib.core.datatype import AttribDict
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.datatype import AttribDict
from lib.core.defaults import _defaults from lib.core.defaults import _defaults
from lib.core.log import FORMATTER
from lib.core.log import LOGGER_HANDLER
from lib.core.log import LOGGER_OUTPUT
from lib.core.exception import SqlmapMissingDependence from lib.core.exception import SqlmapMissingDependence
from lib.core.optiondict import optDict from lib.core.optiondict import optDict
from lib.core.option import init from lib.core.option import init
from lib.core.settings import UNICODE_ENCODING from lib.core.settings import UNICODE_ENCODING
from lib.core.subprocessng import Popen as execute
from lib.core.subprocessng import send_all
from lib.core.subprocessng import recv_some
from thirdparty.bottle.bottle import abort from thirdparty.bottle.bottle import abort
from thirdparty.bottle.bottle import error from thirdparty.bottle.bottle import error
from thirdparty.bottle.bottle import get from thirdparty.bottle.bottle import get
@ -49,13 +50,11 @@ RESTAPI_SERVER_PORT = 8775
# Local global variables # Local global variables
adminid = "" adminid = ""
pipes = dict()
procs = dict() procs = dict()
tasks = AttribDict() tasks = AttribDict()
# Generic functions # Generic functions
def jsonize(data):
return json.dumps(data, sort_keys=False, indent=4)
def is_admin(taskid): def is_admin(taskid):
global adminid global adminid
if adminid != taskid: if adminid != taskid:
@ -254,6 +253,7 @@ def scan_start(taskid):
""" """
global tasks global tasks
global procs global procs
global pipes
if taskid not in tasks: if taskid not in tasks:
abort(500, "Invalid task ID") abort(500, "Invalid task ID")
@ -269,8 +269,13 @@ def scan_start(taskid):
# Launch sqlmap engine in a separate thread # Launch sqlmap engine in a separate thread
logger.debug("starting a scan for task ID %s" % taskid) logger.debug("starting a scan for task ID %s" % taskid)
procs[taskid] = Popen("python sqlmap.py --pickle %s" % base64pickle(tasks[taskid]), shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE) pipes[taskid] = os.pipe()
stdout, stderr = procs[taskid].communicate()
# Provide sqlmap engine with the writable pipe for logging
tasks[taskid]["fdLog"] = pipes[taskid][1]
# Launch sqlmap engine
procs[taskid] = execute("python sqlmap.py --pickled-options %s" % base64pickle(tasks[taskid]), shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=False)
return jsonize({"success": True}) return jsonize({"success": True})
@ -279,17 +284,16 @@ def scan_output(taskid):
""" """
Read the standard output of sqlmap core execution Read the standard output of sqlmap core execution
""" """
global pipes
global tasks global tasks
if taskid not in tasks: if taskid not in tasks:
abort(500, "Invalid task ID") abort(500, "Invalid task ID")
sys.stdout.seek(0) stdout = recv_some(procs[taskid], t=1, e=0, stderr=0)
output = sys.stdout.read() stderr = recv_some(procs[taskid], t=1, e=0, stderr=1)
sys.stdout.flush()
sys.stdout.truncate(0)
return jsonize({"output": output}) return jsonize({"stdout": stdout, "stderr": stderr})
@get("/scan/<taskid>/delete") @get("/scan/<taskid>/delete")
def scan_delete(taskid): def scan_delete(taskid):
@ -306,21 +310,50 @@ def scan_delete(taskid):
return jsonize({"success": True}) return jsonize({"success": True})
# Function to handle scans' logs # Functions to handle scans' logs
@get("/scan/<taskid>/log/<start>/<end>")
def scan_log_limited(taskid, start, end):
"""
Retrieve the log messages
"""
log = None
if taskid not in tasks:
abort(500, "Invalid task ID")
if not start.isdigit() or not end.isdigit() or end <= start:
abort(500, "Invalid start or end value, must be digits")
start = max(0, int(start)-1)
end = max(1, int(end))
pickledLog = os.read(pipes[taskid][0], 100000)
try:
log = base64unpickle(pickledLog)
log = log[slice(start, end)]
except (KeyError, IndexError, TypeError), e:
logger.error("handled exception when trying to unpickle logger dictionary in scan_log_limited(): %s" % str(e))
return jsonize({"log": log})
@get("/scan/<taskid>/log") @get("/scan/<taskid>/log")
def scan_log(taskid): def scan_log(taskid):
""" """
Retrieve the log messages Retrieve the log messages
""" """
log = None
if taskid not in tasks: if taskid not in tasks:
abort(500, "Invalid task ID") abort(500, "Invalid task ID")
LOGGER_OUTPUT.seek(0) pickledLog = os.read(pipes[taskid][0], 100000)
output = LOGGER_OUTPUT.read()
LOGGER_OUTPUT.flush()
LOGGER_OUTPUT.truncate(0)
return jsonize({"log": output}) try:
log = base64unpickle(pickledLog)
except (KeyError, IndexError, TypeError), e:
logger.error("handled exception when trying to unpickle logger dictionary in scan_log(): %s" % str(e))
return jsonize({"log": log})
# Function to handle files inside the output directory # Function to handle files inside the output directory
@get("/download/<taskid>/<target>/<filename:path>") @get("/download/<taskid>/<target>/<filename:path>")