#!/usr/bin/python

import os
import threading
import subprocess
import re
import argparse
import netaddr
import random
import sys
import socket
import time
import datetime
import traceback
from argparse import RawTextHelpFormatter

__version__ = '1.6.4'
print"""
      (`-.     ('-.      .-')
    _(OO  )_ _(  OO)    ( OO )
,--(_/   ,. (,------,--./ ,-- ,--. ,--.,--.   ,--.  .. --. ,--.  ,--,
\   \   /(__/|  .---|   \ |  ||  .'   /|   `.'   | |  -.  \|   \ |  |
 \   \ /   / |  |   |    \|  ||      / |         . ' '  |  |    \|  |
  \   '   /,(|  '--.|  .     ||     '  |  |'.'|  | | |_.'  |  .     |
   \     /__)|  .--'|  |\    ||  .   \ |  |   |  | |  .-.  |  |\    |
    \   /    |  `---|  | \   ||  |\   \|  |   |  | |  | |  |  | \   |
     `-'     `------`--'  `--'`--' '--'`--'   `--' `--' `--`--'  `--'
"""
print '''
We be hunting Ghosts!

Used to hunt accounts that could be used for privilege escalation. It will check
services on remote hosts and the current and reacently logged in users for
accounts of interest.

Author: Darryl Lane
Twitters: @darryllane101
Github: https://github.com/darryllane/venkman
Version: {}
'''.format(__version__)
parser = argparse.ArgumentParser(formatter_class=RawTextHelpFormatter)

group = parser.add_mutually_exclusive_group(required=True)
group2 = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-sl', help='Used to supply a file containing all servers of interest. One per line',  required=False)
group.add_argument('-s', help='Used to supply one server of interest inline',  required=False)
group.add_argument('-r', help='Used to supply a CIDR range of potential targets',  required=False)
parser.add_argument('-b', help='Debugging',  required=False, action='store_true')
parser.add_argument('-a', help='Used for audit purposes, this will print the details of each identified service.',  required=False, action='store_true')
parser.add_argument('-u', help='The username to authenticate to targets\nExample: DOMAIN\username',  required=True)
parser.add_argument('-p', help='The password to authenticate to targets',  required=True)
group2.add_argument('-nl', help='Used to supply a file containing names of interest. One per line',  required=False)
group2.add_argument('-n', help='Used to supply one name of interest inline',  required=False)
args = vars(parser.parse_args())


def percentage(part, whole):
	return int(round(100 * float(part)/float(whole)))


def are_you_up(ip):
	rangei = ['0.1', '0.2', '0.3', '0.4', '0.5']
	try:
		s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		socketTimeout = random.choice(rangei)
		s.settimeout(float(socketTimeout))
		s.connect((ip, 135))
		sys.stdout.write("\r")
		return True
	except socket.error, e:
		items = ['/', '\\', '|']
		sys.stdout.write("\r{}".format(random.choice(items)))
		sys.stdout.flush()


def get_ups(serverList):
	ips = []
	print '\nChecking Who\'s Alive\n'
	for ip in serverList:
		if are_you_up(ip):
			ips.append(ip)
	print 'The Following Hosts Are Up.\n'
	for ip in ips:
		print ip
	return ips


def unique(seq):
	seen = set()
	seen_add = seen.add
	return [ x for x in seq if x not in seen and not seen_add(x)]


def name_list(filename):
	try:
		name_list = [line.rstrip('\n') for line in open(filename)]
	except Exception, e:
		print e
	return name_list


def server_list(filename):
	if os.path.exists(filename):
		try:
			server_list = [line.rstrip('\n') for line in open(filename)]
		except Exception, e:
			traceback.print_exc()
	else:
		print 'ERROR!\n\tThe Filename: {} in \'-sl\' does not exist.\n\tPlease check and try again.'.format(filename)
		sys.exit()
	return server_list


def action_range(ip_range):
	ip_range_expanded = []
	for ip_addy in netaddr.IPNetwork(ip_range):
		ip_range_expanded.append(str(ip_addy))
	return unique(ip_range_expanded)


def wmi_check(serverList, nameList, user, password):

	alldata = []
	print '\nGathering Service Data'
	for server in serverList:
		print '\n' + str(server)
		for name in nameList:
			if '\\' in name:
				name = str(name).split('\\')[1]
			sid_list = get_sid(server, user, password)
			if sid_list:
				luser_name = get_user(server, user, password, sid_list)
				k = 0
				for ided_user in luser_name:
					if name.lower() in ided_user.lower():
						print 'Logged In: {}'.format(ided_user)
					else:
						if args['b']:
							if k < 1:
								print '\nVerbose Output:'
							else:
								print '\n\tLogged In: {}'.format(ided_user)
			else:
				luser_name = 'none'

			try:

				cmd='wmic -U \'{u}%{p}\' //{s} "Select DisplayName, PathName, ProcessId, Description, startname from win32_service where startname Like \'%{n}%\'"'.format(s=server,u=user,p=password,n=name)
				if args['b']:
					print '\nVerbose Output\n\tService Enumeration:'
					print '\t\tCompleted'
				process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
				output = process.communicate()[0]
				lines = output.splitlines()
				if lines[2]:
					alldata.append((server, lines[2], luser_name))

			except IndexError:
				alldata.append((server, lines, luser_name))
				continue
			except Exception, e:
				print 'ERROR:'
				print server
				print e
				continue
	print '\n'
	return alldata


def get_sid(server,user,password):
	users = []
	connected = False
	command_check = "net rpc registry enumerate 'HKEY_USERS' -S {s} -U '{u}%{p}'".format(s=server,u=user,p=password)
	process_check = subprocess.Popen(command_check, shell=True, stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
	output_check = process_check.communicate()[0]
	line = output_check.splitlines()

	j = 0
	for i in line:
		if 'NT_STATUS_UNSUCCESSFUL' in i:
			print 'Unsuccessfull Connection Attempt: {}'.format(server)

		if 'Classes' not in i:
			if args['b']:
				if j < 1:
					print '\nVerbose Output\n\tHKEY_USERS Enumeration:\n'

				j += 1
				print '\t\t' + i
			pattern = r'(.*?)(S\-\d{1,3}\-\d{1,3}\-\d{1,3}\-)(.*)'
			matches = re.match(pattern,i)

			if matches:
				sid = matches.group(2) + matches.group(3)
				connected = True
				users.append((connected, sid))

	if not connected:
		sid = 'Unknown'
		return (connected, sid)
	return (users)



def get_user(server, user, password, sid_list):
	identified_users = []
	for sid_user in sid_list:
		try:
			if sid_user == False:
				raise Exception('NoUserError')
			command_check = 'rpcclient -U "{u}%{p}" {s} -c "lookupsids {sid}"'.format(s=server,u=user,p=password, sid=sid_user[1])
			process_check = subprocess.Popen(command_check, shell=True, stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
			output_check = process_check.communicate()[0]
			line = output_check.splitlines()
			j = 0
			for i in line:
				if "Can't load" not in i:
					pattern = r'(.*?)\s(.*)\s'
					matches = re.match(pattern,i)
					if args['b']:
						if j < 1:
							print '\nVerbose Output\n\tSID Lookups:\n'
						j += 1
						print '\t\tSID: ' + i.split(' ')[0]
					if matches:
						user_name = matches.group(2)
						if args['b']:
							if j < 1:
								print '\nVerbose Output\n\tIdentified Users:\n'
							j += 1
							print '\t\tUser: ' + user_name
						identified_users.append(user_name)
		except Exception('NoUserError'):
			continue
		except Exception,e:
			if 'NoUserError' in e:
				continue
			else:
				print e

	return identified_users


if __name__ == "__main__":

	user = args['u']
	password = args['p']
	if args['sl']:
		serverList = server_list(args['sl'])
		serverList = get_ups(serverList)
	if args['s']:
		serverList = [args['s'], ]
	if args['r']:
		start_time_scope = time.time()
		for item in args['r']:
			if ',' in item:
				args['r'].append(item.strip(','))
				args['r'].remove(item)
		serverList = action_range(args['r'])
		serverList = get_ups(serverList)

		time_spent_total = time.time() - start_time_scope
		timetaken = str(datetime.timedelta(seconds=(time_spent_total))).split('.')[0]
		print '\nComplete: {}'.format(timetaken)
	if args['nl']:
		nameList = name_list(args['nl'])
	if args['n']:
		nameList = [args['n'], ]
	services = []
	final_set = []
	tiab = []
	alldata = wmi_check(serverList, nameList, user, password)
	for item in alldata:
		if item[1] == []:
			final_set.append((item[0], 'None', item[2]))
		else:
			final_set.append((item[0], item[1][0].split('|'), item[2]))


	for value in final_set:
		try:
			Description = value[1][0]
			DisplayName = value[1][1]
			IdAccount = value[1][5]
			PathName = value[1][3]
			pid = value[1][4]
			if pid == '0':
				state = 'stopped'
			else:
				state = 'running'
			tiab.append((pid, value[0], value[2], state, DisplayName, Description, PathName, IdAccount))
		except IndexError:
			continue
		except Exception,e:
			print e

	k = 0
	for item in tiab:
		if k < 1:
			print 'Analysis Completed\n'
		if args['a']:
			print 'Server: ' + item[1]
			print 'Logged On: ' + item[2]
			print 'Account: ' + item[7]
			print 'Service Name: ' + item[4]
			print 'Description: ' + item[5]
			print 'ServicePath: ' + item[6]
			print 'PID: ' + item[0]
			print 'State: ' + item[3]
			print '\n'
		else:
			print 'Server: ' + item[1]
			print 'Logged On: ' + item[2]
			print 'Account: ' + item[7]
			print '\n'
