-
Notifications
You must be signed in to change notification settings - Fork 30
/
utility_windows.go
322 lines (287 loc) Β· 9.45 KB
/
utility_windows.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
package main
import (
"bytes"
"github.com/DataDog/datadog-agent/pkg/util/winutil"
"io/ioutil"
"os"
"os/exec"
"strings"
"unsafe"
"github.com/gen2brain/beeep"
wapi "github.com/iamacarpet/go-win64api"
"github.com/iamacarpet/go-win64api/shared"
"github.com/pkg/errors"
"golang.org/x/sys/windows"
"golang.org/x/text/encoding/unicode"
"golang.org/x/text/transform"
)
var (
kernel32DLL = windows.NewLazyDLL("Kernel32.dll")
debuggerCheck = kernel32DLL.NewProc("IsDebuggerPresent")
)
// readFile (Windows) uses ioutil's ReadFile function and passes the returned
// byte sequence to decodeString.
func readFile(filename string) (string, error) {
raw, err := ioutil.ReadFile(filename)
if err != nil {
return "", err
}
return decodeString(string(raw))
}
// decodeString (Windows) attempts to determine the file encoding type
// (typically, UTF-8, UTF-16, or ANSI) and return the appropriately
// encoded string. (HACK)
func decodeString(fileContent string) (string, error) {
// If contains ~>40% null bytes, we're gonna assume its Unicode
raw := []byte(fileContent)
index := bytes.IndexByte(raw, 0)
if index >= 0 {
nullCount := 0
for _, byteChar := range raw {
if byteChar == 0 {
nullCount++
}
}
percentNull := float32(nullCount) / float32(len(raw))
if percentNull < 0.40 {
return string(raw), nil
}
} else {
return string(raw), nil
}
// Make an tranformer that converts MS-Win default to UTF8
win16be := unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM)
// Make a transformer that is like win16be, but abides by BOM
utf16bom := unicode.BOMOverride(win16be.NewDecoder())
// Make a Reader that uses utf16bom
unicodeReader := transform.NewReader(bytes.NewReader(raw), utf16bom)
// Decode and print
decoded, err := ioutil.ReadAll(unicodeReader)
return string(decoded), err
}
// checkTrace runs WinAPI function "IsDebuggerPresent" to check for an attached
// debugger.
func checkTrace() {
result, _, _ := debuggerCheck.Call()
if int(result) != 0 {
fail("Reversing is cool, but we would appreciate if you practiced your skills in an environment that was less destructive to other peoples' experiences.")
os.Exit(1)
}
}
// sendNotification (Windows) employs the beeep library to send notifications
// to the end user.
func sendNotification(messageString string) {
err := beeep.Notify("Aeacus SE", messageString, dirPath+"assets/img/logo.png")
if err != nil {
fail("Notification error: " + err.Error())
}
}
// rawCmd returns a exec.Command object with the correct PowerShell flags.
//
// rawCmd uses PowerShell's ScriptBlock feature (along with -NoProfile to
// speed things up, as well as some other flags) to run commands on the host
// system and retrieve the return value.
func rawCmd(commandGiven string) *exec.Cmd {
cmdInput := "powershell.exe -NonInteractive -NoProfile Invoke-Command -ScriptBlock { " + commandGiven + " }"
debug("rawCmd input: " + cmdInput)
return exec.Command("powershell.exe", "-NonInteractive", "-NoProfile", "Invoke-Command", "-ScriptBlock", "{ "+commandGiven+" }")
}
// playAudio plays a .wav file with the given path with PowerShell.
func playAudio(wavPath string) {
info("Playing audio:", wavPath)
commandText := "(New-Object Media.SoundPlayer '" + wavPath + "').PlaySync();"
shellCommand(commandText)
}
// adminCheck (Windows) will attempt to open:
//
// \\.\PHYSICALDRIVE0
//
// and will return true if this succeeds, which means the process is running
// as Administrator.
func adminCheck() bool {
_, err := os.Open("\\\\.\\PHYSICALDRIVE0")
return err == nil
}
// sidToLocalUser takes an SID as a string and returns a string containing the
// username of the Local User (NTAccount) that it belongs to.
func sidToLocalUser(sid string) string {
cmdText := "$objSID = New-Object System.Security.Principal.SecurityIdentifier('" + sid + "'); $objUser = $objSID.Translate([System.Security.Principal.NTAccount]); Write-Host $objUser.Value"
output, _ := shellCommandOutput(cmdText)
return strings.TrimSpace(output)
}
// localUserToSid takes a username as a string and returns a string containing
// its SID. This is the opposite of sidToLocalUser.
func localUserToSid(userName string) (string, error) {
return shellCommandOutput("$objUser = New-Object System.Security.Principal.NTAccount('" + userName + "'); $strSID = $objUser.Translate([System.Security.Principal.SecurityIdentifier]); Write-Host $strSID.Value")
}
// getSecedit returns the string value of the secedit.exe command:
//
// secedit.exe /export
//
// which contains security policy options that can't be found in the registry.
func getSecedit() (string, error) {
return shellCommandOutput("secedit.exe /export /cfg sec.cfg /log NUL; Get-Content sec.cfg; Remove-Item sec.cfg")
}
// getNetUserInfo returns the string output from the command:
//
// net user {username}
//
// in order to get user properties and details.
func getNetUserInfo(userName string) (string, error) {
return shellCommandOutput("net user " + userName)
}
// getPrograms returns a list of currently installed Programs and
// their versions.
func getPrograms() ([]string, error) {
softwareList := []string{}
sw, err := wapi.InstalledSoftwareList()
if err != nil {
fail("Couldn't get programs: " + err.Error())
return softwareList, err
}
for _, s := range sw {
softwareList = append(softwareList, s.Name()+" - version "+s.DisplayVersion)
}
return softwareList, nil
}
// getProgram returns the Software struct of program data from a name. The first
// Program that contains the substring passed as the programName is returned.
func getProgram(programName string) (shared.Software, error) {
prog := shared.Software{}
sw, err := wapi.InstalledSoftwareList()
if err != nil {
fail("Couldn't get programs: " + err.Error())
}
for _, s := range sw {
if strings.Contains(s.Name(), programName) {
return s, nil
}
}
return prog, errors.New("program not found")
}
func getLocalUsers() ([]shared.LocalUser, error) {
ul, err := wapi.ListLocalUsers()
if err != nil {
fail("Couldn't get local users: " + err.Error())
}
return ul, err
}
func getLocalAdmins() ([]shared.LocalUser, error) {
ul, err := wapi.ListLocalUsers()
if err != nil {
fail("Couldn't get local users: " + err.Error())
}
var admins []shared.LocalUser
for _, user := range ul {
if user.IsAdmin {
admins = append(admins, user)
}
}
return admins, err
}
func getLocalUser(userName string) (shared.LocalUser, error) {
userList, err := getLocalUsers()
if err != nil {
return shared.LocalUser{}, err
}
for _, user := range userList {
if user.Username == userName {
return user, nil
}
}
return shared.LocalUser{}, nil
}
func getLocalServiceStatus(serviceName string) (shared.Service, error) {
serviceDataList, err := wapi.GetServices()
var serviceStatusData shared.Service
if err != nil {
fail("Couldn't get local service: " + err.Error())
return serviceStatusData, err
}
for _, v := range serviceDataList {
if v.SCName == serviceName {
return v, nil
}
}
fail(`Specified service '` + serviceName + `' was not found on the system`)
return serviceStatusData, err
}
func getFileAccessFromMask(mask uint32) string {
var ret []string
if mask == 0x1f01ff {
ret = append(ret, "FullControl")
} else if mask == 0x1301bf {
ret = append(ret, "Modify")
} else {
if mask&windows.FILE_WRITE_ATTRIBUTES != 0 {
ret = append(ret, "Write")
}
if mask&windows.FILE_READ_ATTRIBUTES != 0 {
if mask&0x000020 != 0 {
ret = append(ret, "ReadAndExecute")
} else {
ret = append(ret, "Read")
}
}
}
return strings.Join(ret, ", ")
}
func getFileOwner(path string) (string, error) {
var sid *windows.SID
if err := winutil.GetNamedSecurityInfo(path,
winutil.SE_FILE_OBJECT,
winutil.OWNER_SECURITY_INFORMATION,
&sid,
nil,
nil,
nil,
nil); err != nil {
return "", errors.Wrapf(err, "acl: failed to get security info for '%s' ", path)
}
var ownerAccount, _, _, err = sid.LookupAccount("")
if err != nil {
return "", errors.Wrapf(err, "Failed to find account for SID '%s'", sid.String())
}
return ownerAccount, nil
}
func getFileRights(filePath, username string) (map[string]string, error) {
userSID, _, _, err := windows.LookupSID("", username)
if err != nil {
return nil, errors.Wrapf(err, "acl: failed to lookup sid for user '%s'", username)
}
ret := map[string]string{}
// Partially adopted from https://github.com/DataDog/datadog-agent
var fileDACL *winutil.Acl
if err := winutil.GetNamedSecurityInfo(filePath,
winutil.SE_FILE_OBJECT,
winutil.DACL_SECURITY_INFORMATION,
nil,
nil,
&fileDACL,
nil,
nil); err != nil {
return nil, errors.Wrapf(err, "acl: failed to get security info for '%s' ", filePath)
}
var aclSizeInfo winutil.AclSizeInformation
if err := winutil.GetAclInformation(fileDACL, &aclSizeInfo, winutil.AclSizeInformationEnum); err != nil {
return nil, errors.Wrapf(err, "acl: failed to get acl size info for '%s' ", filePath)
}
for i := uint32(0); i < aclSizeInfo.AceCount; i++ {
var pACE *winutil.AccessAllowedAce
if err := winutil.GetAce(fileDACL, i, &pACE); err != nil {
return nil, errors.Wrapf(err, "acl: failed to get acl for '%s' ", filePath)
}
sid := (*windows.SID)(unsafe.Pointer(&pACE.SidStart))
if strings.EqualFold(userSID.String(), sid.String()) {
ret["identityreference"] = username
if pACE.AceType == winutil.ACCESS_DENIED_ACE_TYPE {
ret["accesscontroltype"] = "Deny"
}
if pACE.AceType == winutil.ACCESS_ALLOWED_ACE_TYPE {
ret["accesscontroltype"] = "Allow"
}
ret["filesystemrights"] = getFileAccessFromMask(pACE.AccessMask)
}
}
return ret, nil
}