kgretzky/evilginx2 · error

phishlet '%s' can't be deleted - you can only delete child p

Error message

phishlet '%s' can't be deleted - you can only delete child phishlets.

What it means

DeleteSubPhishlet only allows deletion of child (inherited) phishlets. If the phishlet identified by `site` has no ParentName (i.e. it is a top-level phishlet), the config refuses to delete it and returns this error, protecting base phishlets from accidental removal.

Source

Thrown at core/config.go:592

	if err != nil {
		return err
	}
	sub_pl.ParentName = parent_site

	c.phishletNames = append(c.phishletNames, site)
	c.phishlets[site] = sub_pl
	c.VerifyPhishlets()

	return nil
}

func (c *Config) DeleteSubPhishlet(site string) error {
	pl, err := c.GetPhishlet(site)
	if err != nil {
		return err
	}
	if pl.ParentName == "" {
		return fmt.Errorf("phishlet '%s' can't be deleted - you can only delete child phishlets.", site)
	}

	c.phishletNames = removeString(site, c.phishletNames)
	delete(c.phishlets, site)
	delete(c.phishletConfig, site)
	c.SavePhishlets()
	return nil
}

func (c *Config) LoadSubPhishlets() {
	var subphishlets []*SubPhishlet
	c.cfg.UnmarshalKey(CFG_SUBPHISHLETS, &subphishlets)
	for _, spl := range subphishlets {
		err := c.AddSubPhishlet(spl.Name, spl.ParentName, spl.Params)
		if err != nil {
			log.Error("phishlets: %s", err)
		}
	}

View on GitHub (pinned to 4c0988a1d9)

Solutions

  1. Only call DeleteSubPhishlet for phishlets whose ParentName is non-empty; delete base phishlets through a different path if the API exposes one
  2. Check c.phishlets[site].ParentName before calling, and skip base phishlets
  3. Verify the site name refers to the child phishlet, not its parent

Example fix

// before
cfg.DeleteSubPhishlet("linkedin")
// after
pl, _ := cfg.GetPhishlet("linkedin")
if pl.ParentName != "" {
    cfg.DeleteSubPhishlet("linkedin")
}
Defensive patterns

Strategy: validation

Validate before calling

func canDeleteSub(cfg *core.Config, site string) bool {
    pl, err := cfg.GetPhishlet(site)
    return err == nil && pl != nil && pl.ParentName != ""
}

Try / catch

if err := cfg.DeleteSubPhishlet(site); err != nil {
    log.Printf("skip %s: %v", site, err)
}

Prevention

When it happens

Trigger: Calling cfg.DeleteSubPhishlet(site) with the name of a standalone/base phishlet (one created directly, not via CreateSubPhishlet), so pl.ParentName == "".

Common situations: Developers managing evilginx-style phishlet trees iterate over all phishlet names and call DeleteSubPhishlet on each, hitting the base phishlets; or they pass a typo'd name that resolves to a base phishlet instead of the intended child.

Related errors


AI-assisted analysis of kgretzky/evilginx2@4c0988a1d9 (2026-09-05). Data as JSON: /api/errors/4f006ea6e4426dd5. Report an issue: GitHub.