shadow1ng/fscan · error

local_pe_not_found

Error message

local_pe_not_found

What it means

WinLogonPlugin.Scan verifies the configured PE path exists with os.Stat before writing HKLM Winlogon registry entries. If Stat fails, Scan returns a failed Result with the localized "local_pe_not_found" message. This guards against configuring a Winlogon hijack to a missing executable.

Source

Thrown at plugins/local/winlogon.go:32

	"github.com/shadow1ng/fscan/common/i18n"
	"github.com/shadow1ng/fscan/plugins"
)

type WinLogonPlugin struct {
	plugins.BasePlugin
}

func NewWinLogonPlugin() *WinLogonPlugin {
	return &WinLogonPlugin{BasePlugin: plugins.NewBasePlugin("winlogon")}
}

func (p *WinLogonPlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *plugins.Result {
	pePath := session.Config.WinPEFile
	if pePath == "" {
		return &plugins.Result{Success: false, Error: fmt.Errorf("%s", i18n.GetText("local_pe_not_specified"))}
	}
	if _, err := os.Stat(pePath); err != nil {
		return &plugins.Result{Success: false, Error: fmt.Errorf("%s", i18n.Tr("local_pe_not_found", pePath))}
	}

	absPath, _ := filepath.Abs(pePath)
	key := `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon`

	entries := []struct {
		name  string
		value string
		desc  string
	}{
		{"Userinit", fmt.Sprintf(`C:\Windows\system32\userinit.exe,%s`, absPath), i18n.GetText("winlogon_userinit_append")},
		{"Shell", fmt.Sprintf(`explorer.exe,%s`, absPath), i18n.GetText("winlogon_shell_append")},
	}

	var output strings.Builder
	var successCount int

	for _, e := range entries {

View on GitHub (pinned to 95cc12e753)

Solutions

  1. Verify the file exists locally: Test-Path <pePath>, then correct the WinPEFile value.
  2. Use an absolute path and confirm the drive/share is accessible from the scanning host.
  3. Restore the file from antivirus quarantine or add an exclusion.
  4. Re-check permissions on the file and its directories for the running user.

Example fix

// before
session.Config.WinPEFile = "\\\\server\\share\\payload.exe" // share unreachable -> Stat fails
// after
p := "C:\\tools\\payload.exe"
if _, err := os.Stat(p); err != nil {
    log.Fatalf("WinPEFile invalid: %v", err)
}
session.Config.WinPEFile = p
Defensive patterns

Strategy: validation

Validate before calling

// Check the Winlogon payload path exists and is a regular file
fi, err := os.Stat(session.Config.WinPEFile)
if err != nil {
    return fmt.Errorf("WinPEFile missing: %w", err)
}
if !fi.Mode().IsRegular() {
    return fmt.Errorf("WinPEFile is not a regular file")

Try / catch

result := plugin.Scan(ctx, host, session)
if result != nil && !result.Success && strings.Contains(result.Error.Error(), i18n.GetText("local_pe_not_found")) {
    // correct the configured path and re-run
}

Prevention

When it happens

Trigger: os.Stat(pePath) returns an error for the non-empty WinPEFile value — path typo, file deleted/quarantined, relative path broken by a different working directory, or inaccessible drive/permissions.

Common situations: Path referencing the target host's filesystem instead of the local one, UNC path unreachable, antivirus removing the binary, or a renamed file after the config was written.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of shadow1ng/fscan@95cc12e753 (2026-09-06). Data as JSON: /api/errors/a50842616e822f11. Report an issue: GitHub.