328 lines
12 KiB
Python
Executable File
328 lines
12 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# coding=utf-8
|
|
# pyvncs
|
|
# Copyright (C) 2017-2018 Matias Fernandez
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify it under
|
|
# the terms of the GNU Lesser General Public License as published by the Free
|
|
# Software Foundation, either version 3 of the License, or (at your option) any
|
|
# later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
|
# details.
|
|
#
|
|
# You should have received a copy of the GNU Lesser General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import pyvncs
|
|
from lib import log
|
|
from lib import common
|
|
from argparse import ArgumentParser
|
|
from threading import Thread
|
|
import time
|
|
import sys
|
|
import socket
|
|
import signal
|
|
import readline
|
|
import traceback
|
|
|
|
#_debug = log.debug
|
|
_debug = print
|
|
|
|
if common.isWindows():
|
|
_debug("Wintendo...")
|
|
import win32ts
|
|
import win32security
|
|
import win32con
|
|
import win32api
|
|
import ntsecuritycon
|
|
import win32process
|
|
import win32event
|
|
|
|
def signal_handler(signal, frame):
|
|
_debug("Exiting on signal %s..." % signal)
|
|
sys.exit(0)
|
|
|
|
signal.signal(signal.SIGINT, signal_handler)
|
|
|
|
config = {
|
|
}
|
|
|
|
class ControlThread(Thread):
|
|
def __init__(self, threads):
|
|
Thread.__init__(self)
|
|
self.threads = threads
|
|
self.setDaemon(True)
|
|
|
|
def run(self):
|
|
# elimina los threads muertos
|
|
while True:
|
|
time.sleep(1)
|
|
for t in threads:
|
|
if not t.isAlive():
|
|
_debug("ControlThread removing dead", t)
|
|
threads.remove(t)
|
|
|
|
class VNCThread(Thread):
|
|
def __init__(self, port, password):
|
|
Thread.__init__(self)
|
|
self.ip = None
|
|
self.port = port
|
|
self.sock = None
|
|
self.password = password
|
|
self.setDaemon(True)
|
|
|
|
def __del__(self):
|
|
_debug("VNCThread died")
|
|
|
|
|
|
def run(self):
|
|
|
|
TCP_IP = '0.0.0.0'
|
|
_debug("[+] Listen...")
|
|
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
self.sock.bind((TCP_IP, int(self.port)))
|
|
self.sock.listen(4)
|
|
(conn, (ip,port)) = self.sock.accept()
|
|
|
|
_debug("[+] New server socket started for " + ip + ":" + str(port))
|
|
#_debug("Thread", self)
|
|
server = pyvncs.server.VncServer(conn, self.password)
|
|
#server.CONFIG._8bitdither = CONFIG._8bitdither
|
|
status = server.init()
|
|
|
|
if not status:
|
|
_debug("Error negotiating client init")
|
|
return False
|
|
server.protocol()
|
|
|
|
|
|
class ClientThread(Thread):
|
|
def __init__(self, sock, ip, port, config):
|
|
Thread.__init__(self)
|
|
self.ip = ip
|
|
self.port = port
|
|
self.sock = sock
|
|
self.setDaemon(True)
|
|
self.config = config
|
|
|
|
def __del__(self):
|
|
_debug("ClientThread died")
|
|
|
|
def run(self):
|
|
#_debug("[+] New server socket thread started for " + self.ip + ":" + str(self.port))
|
|
#_debug("Thread", self)
|
|
f = self.sock.makefile('rw')
|
|
|
|
f.write("AUTH:>")
|
|
f.flush()
|
|
passwd = f.readline().strip("\n")
|
|
if passwd != config["PASSWORD"]:
|
|
time.sleep(1)
|
|
f.write("!NO AUTH")
|
|
f.flush()
|
|
_debug("NO AUTH '%s' != '%s'" % (passwd, config["PASSWORD"]))
|
|
self.sock.close()
|
|
return
|
|
|
|
while True:
|
|
f.write("OK:>")
|
|
f.flush()
|
|
try:
|
|
data = f.readline()
|
|
cmd = data.strip()
|
|
if not data: break
|
|
|
|
if cmd == "_DEBUG":
|
|
sys.stdout = sys.stderr = f
|
|
f.write("OK\n")
|
|
|
|
elif cmd == "PING":
|
|
f.write("PONG\n")
|
|
|
|
elif cmd == "QUIT":
|
|
f.write("BYE\n")
|
|
self.sock.close()
|
|
return
|
|
|
|
elif cmd.startswith("STARTVNC"):
|
|
params = cmd.split()
|
|
if len(params) != 3:
|
|
f.write("!NOT_PARAMS\n")
|
|
f.flush()
|
|
continue
|
|
_debug("START VNC !!!")
|
|
newthread = VNCThread(params[1], params[2])
|
|
newthread.setDaemon(True)
|
|
newthread.start()
|
|
|
|
elif cmd == "_WINSESSIONS" and common.isWindows():
|
|
winsessions = win32ts.WTSEnumerateSessions(win32ts.WTS_CURRENT_SERVER_HANDLE)
|
|
print(winsessions, file=f)
|
|
|
|
elif cmd == "_WINCONSOLE" and common.isWindows():
|
|
winsessions = win32ts.WTSEnumerateSessions(win32ts.WTS_CURRENT_SERVER_HANDLE)
|
|
print(winsessions, file=f)
|
|
active = win32ts.WTSGetActiveConsoleSessionId()
|
|
print(active, file=f)
|
|
token = win32ts.WTSQueryUserToken(active)
|
|
print(token, file=f)
|
|
duplicated = win32security.DuplicateTokenEx(token, win32con.MAXIMUM_ALLOWED, win32con.NULL, win32security.TokenPrimary, win32security.SECURITY_ATTRIBUTES())
|
|
print("duplicated", token, file=f)
|
|
|
|
elif cmd == "_TEST" and common.isWindows():
|
|
winsessions = win32ts.WTSEnumerateSessions(win32ts.WTS_CURRENT_SERVER_HANDLE)
|
|
print(winsessions, file=f)
|
|
active = win32ts.WTSGetActiveConsoleSessionId()
|
|
print(active, file=f)
|
|
token = win32ts.WTSQueryUserToken(active)
|
|
print("token", token, file=f)
|
|
ntoken = win32security.DuplicateTokenEx(token, 3 , win32con.MAXIMUM_ALLOWED , win32security.TokenPrimary , win32security.SECURITY_ATTRIBUTES() )
|
|
print("ntoken", ntoken, file=f)
|
|
#th = win32security.OpenProcessToken(win32api.GetCurrentProcess(), win32con.MAXIMUM_ALLOWED)
|
|
#print("th", th, file=f)
|
|
shell_as2(ntoken, True, "cmd")
|
|
|
|
elif cmd == "_EVAL":
|
|
while True:
|
|
f.write("EV:>")
|
|
f.flush()
|
|
data = f.readline()
|
|
cmd = data.strip()
|
|
if cmd == "": continue
|
|
if cmd == "_QUIT": break
|
|
if not data: break
|
|
|
|
try:
|
|
eval(data)
|
|
except:
|
|
print("ERROR:", sys.exc_info()[0], file=f)
|
|
print(traceback.format_exc(), file=f)
|
|
f.flush()
|
|
|
|
_debug("eval:", cmd.strip())
|
|
f.flush()
|
|
|
|
elif cmd == "_ERROR":
|
|
a = 1/0
|
|
|
|
else:
|
|
f.write("!NO_CMD %s\n" % cmd)
|
|
|
|
_debug("command:", cmd.strip())
|
|
f.flush()
|
|
except:
|
|
print("ERROR:", sys.exc_info()[0], file=f)
|
|
print(traceback.format_exc(), file=f)
|
|
f.flush()
|
|
|
|
def get_all_privs(th):
|
|
# Try to give ourselves some extra privs (only works if we're admin):
|
|
# SeBackupPrivilege - so we can read anything
|
|
# SeDebugPrivilege - so we can find out about other processes (otherwise OpenProcess will fail for some)
|
|
# SeSecurityPrivilege - ??? what does this do?
|
|
|
|
# Problem: Vista+ support "Protected" processes, e.g. audiodg.exe. We can't see info about these.
|
|
# Interesting post on why Protected Process aren't really secure anyway: http://www.alex-ionescu.com/?p=34
|
|
|
|
privs = win32security.GetTokenInformation(th, ntsecuritycon.TokenPrivileges)
|
|
for privtuple in privs:
|
|
privs2 = win32security.GetTokenInformation(th, ntsecuritycon.TokenPrivileges)
|
|
newprivs = []
|
|
for privtuple2 in privs2:
|
|
if privtuple2[0] == privtuple[0]:
|
|
newprivs.append((privtuple2[0], 2)) # SE_PRIVILEGE_ENABLED
|
|
else:
|
|
newprivs.append((privtuple2[0], privtuple2[1]))
|
|
|
|
# Adjust privs
|
|
privs3 = tuple(newprivs)
|
|
win32security.AdjustTokenPrivileges(th, False, privs3)
|
|
def shell_as(th, enable_privs = 0):
|
|
#t = thread(th)
|
|
#print(t.as_text())
|
|
new_tokenh = win32security.DuplicateTokenEx(th, 3 , win32con.MAXIMUM_ALLOWED , win32security.TokenPrimary , win32security.SECURITY_ATTRIBUTES() )
|
|
print("new_tokenh: %s" % new_tokenh)
|
|
print("Impersonating...")
|
|
if enable_privs:
|
|
get_all_privs(new_tokenh)
|
|
commandLine = "cmd"
|
|
si = win32process.STARTUPINFO()
|
|
print("pysecdump: Starting shell with required privileges...")
|
|
(hProcess, hThread, dwProcessId, dwThreadId) = win32process.CreateProcessAsUser(
|
|
new_tokenh,
|
|
None, # AppName
|
|
commandLine, # Command line
|
|
None, # Process Security
|
|
None, # ThreadSecurity
|
|
1, # Inherit Handles?
|
|
win32process.NORMAL_PRIORITY_CLASS,
|
|
None, # New environment
|
|
None, # Current directory
|
|
si) # startup info.
|
|
win32event.WaitForSingleObject( hProcess, win32event.INFINITE );
|
|
print("pysecdump: Quitting")
|
|
|
|
def shell_as2(new_tokenh, enable_privs = 0, commandLine = "cmd"):
|
|
print("new_tokenh: %s" % new_tokenh)
|
|
print("Impersonating...")
|
|
if enable_privs:
|
|
get_all_privs(new_tokenh)
|
|
si = win32process.STARTUPINFO()
|
|
print("pysecdump: Starting shell with required privileges...")
|
|
(hProcess, hThread, dwProcessId, dwThreadId) = win32process.CreateProcessAsUser(
|
|
new_tokenh,
|
|
None, # AppName
|
|
commandLine, # Command line
|
|
None, # Process Security
|
|
None, # ThreadSecurity
|
|
1, # Inherit Handles?
|
|
win32process.NORMAL_PRIORITY_CLASS,
|
|
None, # New environment
|
|
None, # Current directory
|
|
si) # startup info.
|
|
win32event.WaitForSingleObject( hProcess, win32event.INFINITE )
|
|
print("pysecdump: Quitting")
|
|
|
|
def main(argv):
|
|
global threads, config
|
|
parser = ArgumentParser()
|
|
parser.add_argument("-l", "--listen-address", dest="TCP_IP",
|
|
help="Listen in this address, default: %s" % ("0.0.0.0"), required=False, default='0.0.0.0')
|
|
parser.add_argument("-p", "--port", dest="TCP_PORT",
|
|
help="Listen in this port, default: %s" % ("5899"), type=int, required=False, default='5899')
|
|
parser.add_argument("-P", "--password", help="Sets password", required=True, dest="PASSWORD")
|
|
|
|
args = parser.parse_args()
|
|
|
|
config["PASSWORD"] = args.PASSWORD
|
|
config["PORT"] = args.TCP_PORT
|
|
|
|
sockServer = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
sockServer.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
sockServer.bind((args.TCP_IP, args.TCP_PORT))
|
|
|
|
controlthread = ControlThread(threads)
|
|
controlthread.start()
|
|
threads.append(controlthread)
|
|
|
|
#_debug("Multithreaded Python server : Waiting for connections from TCP clients...")
|
|
_debug("Runing on:", sys.platform)
|
|
while True:
|
|
sockServer.listen(4)
|
|
(conn, (ip,port)) = sockServer.accept()
|
|
newthread = ClientThread(conn, ip, port, config)
|
|
newthread.setDaemon(True)
|
|
newthread.start()
|
|
threads.append(newthread)
|
|
#print(threads)
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
threads = []
|
|
main(sys.argv)
|