first attempt to have --os-pwn and other takeover switches work across Windows and Linux - issue #28

This commit is contained in:
Bernardo Damele 2013-01-09 16:04:23 +00:00
parent c44a829b9b
commit 510ceb6e19
4 changed files with 214 additions and 65 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

@ -7,13 +7,19 @@ 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:
from win32file import ReadFile, WriteFile
from win32pipe import PeekNamedPipe
import msvcrt
else:
import select
import fcntl import fcntl
if (sys.hexversion >> 16) >= 0x202: if (sys.hexversion >> 16) >= 0x202:
@ -61,30 +67,131 @@ 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 = ''
pr = p.recv
if stderr:
pr = p.recv_err
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

@ -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()