Persistence — A starting point for scanning guidance

// EnumRegistryPeristence get all the potential registry values used for persistencefunc EnumRegistryPeristence() (values []RegistryValue, errors []error) {
keys := []registry.Key{registry.USERS, registry.LOCAL_MACHINE}
subkeys := []string{`SOFTWARE\Microsoft\Windows\CurrentVersion\Run`, `SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce`}
for _, k := range keys {
for _, s := range subkeys {
v, err := EnumRegHivePersistence(k, s)
if err != nil {
errMsg := fmt.Errorf(“%s\\%s — %s”, GetRegistryHiveNameFromConst(k), s, err)
errors = append(errors, errMsg)
}
for _, value := range v {
values = append(values, value)
}
}
}
return values, errors
}
  • SOFTWARE\Microsoft\Windows\CurrentVersion\Run
  • SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce
// EnumRegHivePersistence parse the specified key and subkey and return all string key/value in a []RegistryValuefunc EnumRegHivePersistence(key registry.Key, subkey string) (values []RegistryValue, err error) {
k, err := registry.OpenKey(key, "", registry.READ)
if err != nil {
return nil, err
}
defer k.Close()
subkeys, err := k.ReadSubKeyNames(0)
if err != nil {
return nil, err
}
if key == registry.USERS {
for _, s := range subkeys {
if len(s) > 10 && !strings.HasSuffix(s, "_Classes") {
v, err := enumRegSubKey(key, s+`\`+subkey)
if err != nil {
return nil, err
}
for _, item := range v {
values = append(values, item)
}
}
}
} else {
v, err := enumRegSubKey(key, subkey)
if err != nil {
return nil, err
}
for _, item := range v {
values = append(values, item)
}
}
return values, nil
}
// enumRegSubKey format subkey values in []RegistryValuefunc enumRegSubKey(key registry.Key, subkey string) (values []RegistryValue, err error) {    var (
sk registry.Key
sv []string
)
sk, err = registry.OpenKey(key, subkey, registry.READ)
if err != nil {
return nil, err
}
sv, err = sk.ReadValueNames(0)
if err != nil {
return nil, err
}
for _, item := range sv {
v, _, _ := sk.GetStringValue(item)
if len(v) > 0 {
var result = RegistryValue{key: key, subKey: subkey, valueName: item, value: v}
values = append(values, result)
}
}
return values, nil
}
  • %SYSTEMDRIVE%\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp
  • %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup
// ListStartMenuFolders return a []string of all available StartMenu foldersfunc ListStartMenuFolders(verbose bool) (startMenu []string, err error) {
var usersDir []string
startMenu = append(startMenu, os.Getenv("SystemDrive")+`\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp`)
usersDir, err = RetrivesFilesFromUserPath(os.Getenv("SystemDrive")+`\Users`, false, nil, false, verbose) if err != nil {
return startMenu, err
}
for _, uDir := range usersDir {
startMenu = append(startMenu, uDir+`\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup`)
}
return startMenu, err
}
// ListStartMenuLnkPersistence check for every *.lnk in Windows StartMenu folders and extract every executable path in those linksfunc ListStartMenuLnkPersistence(verbose bool) (exePath []string, errors []error) {
startMenuFolders, err := ListStartMenuFolders(verbose)
if err != nil {
errors = append(errors, err)
}
for _, path := range startMenuFolders {
files, err := RetrivesFilesFromUserPath(path, true, []string{".lnk"}, false, verbose)
if err != nil {
errors = append(errors, fmt.Errorf("%s - %s", path, err.Error()))
}
for _, p := range files {
lnk, lnkErr := golnk.File(p)
if lnkErr != nil {
errors = append(errors, fmt.Errorf("%s - Lnk parse error", p))
continue
}
exePath = append(exePath, lnk.LinkInfo.LocalBasePath)
}
}

return exePath, errors
}
  • “C:\Path\Example\Binary.exe” -attribute
  • “%windir%\system32\Binary.exe”
  • “%windir%\system32\cmd.exe” -c “run c:\Path\To\Binary.exe”
  • “rundll.exe c:\Path\To\file.dll,LockWorkStation”
// FormatPathFromComplexString search for file/directory path and remove environments variables, quotes and extra parametersfunc FormatPathFromComplexString(command string) (paths []string) {
var buffer []string

// quoted path
if strings.Contains(command, `"`) || strings.Contains(command, `'`) {
re := regexp.MustCompile(`[\'\"](.+)[\'\"]`)
matches := re.FindStringSubmatch(command)
for i := range matches {
if i != 0 {
buffer = append(buffer, matches[i])
}
}
} else {
for _, i := range strings.Split(strings.Replace(command, ",", "", -1), " ") {
buffer = append(buffer, i)
}
}
for _, item := range buffer {
// environment variables
if strings.Contains(command, `%`) {
re := regexp.MustCompile(`%(\w+)%`)
res := re.FindStringSubmatch(item)
for i := range res {
item = strings.Replace(item, "%"+res[i]+"%", os.Getenv(res[i]), -1)
}
}
// check if file exists
if _, err := os.Stat(item); !os.IsNotExist(err) {
paths = append(paths, item)
}
}
return paths
}

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store