#!/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 . 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)