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
else:
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 os
import subprocess
import sys
import time
from lib.core.common import dataToStdout
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
if (sys.hexversion >> 16) >= 0x202:
@ -61,30 +67,131 @@ def blockingWriteToFD(fd, data):
break
def setNonBlocking(fd):
"""
Make a file descriptor non-blocking
"""
# the following code is taken from http://code.activestate.com/recipes/440554-module-to-allow-asynchronous-subprocess-use-on-win/
class Popen(subprocess.Popen):
def recv(self, maxsize=None):
return self._recv('stdout', maxsize)
if IS_WIN is not True:
flags = fcntl.fcntl(fd, FCNTL.F_GETFL)
flags = flags | os.O_NONBLOCK
fcntl.fcntl(fd, FCNTL.F_SETFL, flags)
def recv_err(self, maxsize=None):
return self._recv('stderr', maxsize)
def pollProcess(process, suppress_errors=False):
while True:
dataToStdout(".")
time.sleep(1)
def send_recv(self, input='', maxsize=None):
return self.send(input), self.recv(maxsize), self.recv_err(maxsize)
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:
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)
def _close(self, which):
getattr(self, which).close()
setattr(self, which, None)
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
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 lib.core.common import dataToStdout
from lib.core.common import pollProcess
from lib.core.data import conf
from lib.core.data import logger
from lib.core.data import paths
from lib.core.revision import getRevisionNumber
from lib.core.settings import GIT_REPOSITORY
from lib.core.settings import IS_WIN
from lib.core.subprocessng import pollProcess
def update():
if not conf.updateAll:

View File

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