CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/scanner/msmail/onprem_enum.go
Views: 1904
1
//usr/bin/env go run "$0" "$@"; exit "$?"
2
3
package main
4
5
import (
6
"crypto/tls"
7
"metasploit/module"
8
"msmail"
9
"net/http"
10
"sort"
11
"strconv"
12
"sync"
13
"time"
14
)
15
16
func main() {
17
metadata := &module.Metadata{
18
Name: "On premise user enumeration",
19
Description: "On premise enumeration of valid exchange users",
20
Authors: []string{"poptart", "jlarose", "Vincent Yiu", "grimhacker", "Nate Power", "Nick Powers", "clee-r7"},
21
Date: "2018-11-06",
22
Type: "single_scanner",
23
Privileged: false,
24
References: []module.Reference{},
25
Options: map[string]module.Option{
26
"USERNAME": {Type: "string", Description: "Single user name to do identity test against", Required: false, Default: ""},
27
"USER_FILE": {Type: "string", Description: "Path to file containing list of users", Required: false, Default: ""},
28
}}
29
30
module.Init(metadata, run_onprem_enum)
31
}
32
33
func run_onprem_enum(params map[string]interface{}) {
34
userFile := params["USER_FILE"].(string)
35
userName := params["USERNAME"].(string)
36
host := params["rhost"].(string)
37
threads, e := strconv.Atoi(params["THREADS"].(string))
38
if e != nil {
39
module.LogError("Unable to parse 'Threads' value using default (5)")
40
threads = 5
41
}
42
43
if threads > 100 {
44
module.LogInfo("Threads value too large, setting max(100)")
45
threads = 100
46
}
47
48
if userFile == "" && userName == "" {
49
module.LogError("Expected 'USER_FILE' or 'USERNAME' field to be populated")
50
return
51
}
52
53
var validUsers []string
54
avgResponse := basicAuthAvgTime(host)
55
if userFile != "" {
56
validUsers = determineValidUsers(host, avgResponse, msmail.ImportUserList(userFile), threads)
57
} else {
58
validUsers = determineValidUsers(host, avgResponse, []string{userName}, threads)
59
}
60
61
msmail.ReportValidUsers(host, validUsers)
62
}
63
64
func determineValidUsers(host string, avgResponse time.Duration, userlist []string, threads int) []string {
65
limit := threads
66
var wg sync.WaitGroup
67
queue := make(chan string)
68
69
/*Keep in mind you, nothing has been added to handle successful auths
70
so the password for auth attempts has been hardcoded to something
71
that is not likely to be correct.
72
*/
73
pass := "Summer2018978"
74
internaldomain := msmail.HarvestInternalDomain(host, false)
75
url1 := "https://" + host + "/autodiscover/autodiscover.xml"
76
url2 := "https://" + host + "/Microsoft-Server-ActiveSync"
77
url3 := "https://autodiscover." + host + "/autodiscover/autodiscover.xml"
78
var urlToHarvest string
79
if msmail.WebRequestCodeResponse(url1) == 401 {
80
urlToHarvest = url1
81
} else if msmail.WebRequestCodeResponse(url2) == 401 {
82
urlToHarvest = url2
83
} else if msmail.WebRequestCodeResponse(url3) == 401 {
84
urlToHarvest = url3
85
} else {
86
module.LogInfo("Unable to resolve host provided to determine valid users.")
87
return []string{}
88
}
89
var validusers []string
90
tr := &http.Transport{
91
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
92
}
93
for i := 0; i < limit; i++ {
94
wg.Add(1)
95
go func(i int) {
96
defer wg.Done()
97
for user := range queue {
98
startTime := time.Now()
99
msmail.WebRequestBasicAuth(urlToHarvest, internaldomain+"\\"+user, pass, tr)
100
elapsedTime := time.Since(startTime)
101
102
if float64(elapsedTime) < float64(avgResponse)*0.77 {
103
module.LogGood(user + " - " + elapsedTime.String())
104
validusers = append(validusers, user)
105
} else {
106
module.LogError(user + " - " + elapsedTime.String())
107
}
108
}
109
}(i)
110
}
111
112
for i := 0; i < len(userlist); i++ {
113
queue <- userlist[i]
114
}
115
116
close(queue)
117
wg.Wait()
118
return validusers
119
}
120
121
func basicAuthAvgTime(host string) time.Duration {
122
internaldomain := msmail.HarvestInternalDomain(host, false)
123
url1 := "https://" + host + "/autodiscover/autodiscover.xml"
124
url2 := "https://" + host + "/Microsoft-Server-ActiveSync"
125
url3 := "https://autodiscover." + host + "/autodiscover/autodiscover.xml"
126
var urlToHarvest string
127
if msmail.WebRequestCodeResponse(url1) == 401 {
128
urlToHarvest = url1
129
} else if msmail.WebRequestCodeResponse(url2) == 401 {
130
urlToHarvest = url2
131
} else if msmail.WebRequestCodeResponse(url3) == 401 {
132
urlToHarvest = url3
133
} else {
134
module.LogInfo("Unable to resolve host provided to determine valid users.")
135
return -1
136
}
137
138
//We are determining sample auth response time for invalid users, the password used is irrelevant.
139
pass := "Summer201823904"
140
tr := &http.Transport{
141
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
142
}
143
module.LogInfo("Collecting sample auth times...")
144
145
var sliceOfTimes []float64
146
var medianTime float64
147
148
usernamelist := []string{"sdfsdskljdfhkljhf", "ssdlfkjhgkjhdfsdfw", "sdfsdfdsfff", "sefsefsefsss", "lkjhlkjhiuyoiuy", "khiuoiuhohuio", "s2222dfs45g45gdf", "sdfseddf3333"}
149
for i := 0; i < len(usernamelist)-1; i++ {
150
startTime := time.Now()
151
msmail.WebRequestBasicAuth(urlToHarvest, internaldomain+"\\"+usernamelist[i], pass, tr)
152
elapsedTime := time.Since(startTime)
153
if elapsedTime > time.Second*15 {
154
module.LogInfo("Response taking longer than 15 seconds, setting time:")
155
module.LogInfo("Avg Response: " + time.Duration(elapsedTime).String())
156
return time.Duration(elapsedTime)
157
}
158
if i != 0 {
159
module.LogInfo(elapsedTime.String())
160
sliceOfTimes = append(sliceOfTimes, float64(elapsedTime))
161
}
162
}
163
sort.Float64s(sliceOfTimes)
164
if len(sliceOfTimes)%2 == 0 {
165
positionOne := len(sliceOfTimes)/2 - 1
166
positionTwo := len(sliceOfTimes) / 2
167
medianTime = (sliceOfTimes[positionTwo] + sliceOfTimes[positionOne]) / 2
168
} else if len(sliceOfTimes)%2 != 0 {
169
position := len(sliceOfTimes)/2 - 1
170
medianTime = sliceOfTimes[position]
171
} else {
172
module.LogError("Error determining whether length of times gathered is even or odd to obtain median value.")
173
}
174
module.LogInfo("Avg Response: " + time.Duration(medianTime).String())
175
return time.Duration(medianTime)
176
}
177
178
179
180