VYPR
High severity7.2OSV Advisory· Published Oct 8, 2025· Updated Apr 15, 2026

CVE-2025-61524

CVE-2025-61524

Description

An issue in the permission verification module and organization/application editing interface in Casdoor v2.26.0 and before, and fixed in v.2.63.0, allows remote authenticated administrators of any organization within the system to bypass the system's permission verification mechanism by directly concatenating URLs after login

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/casdoor/casdoorGo
< 2.63.02.63.0

Affected products

1

Patches

1
d883db907bb6

feat: improve authz_filter (#4195)

https://github.com/casdoor/casdoorDacongDASep 18, 2025via ghsa
11 files changed · +37 13
  • authz/authz.go+4 0 modified
    @@ -143,6 +143,10 @@ func IsAllowed(subOwner string, subName string, method string, urlPath string, o
     			return false
     		}
     
    +		if user.IsGlobalAdmin() {
    +			return true
    +		}
    +
     		if user.IsAdmin && (subOwner == objOwner || (objOwner == "admin")) {
     			return true
     		}
    
  • controllers/application.go+1 1 modified
    @@ -237,7 +237,7 @@ func (c *ApiController) UpdateApplication() {
     		return
     	}
     
    -	c.Data["json"] = wrapActionResponse(object.UpdateApplication(id, &application))
    +	c.Data["json"] = wrapActionResponse(object.UpdateApplication(id, &application, c.IsGlobalAdmin()))
     	c.ServeJSON()
     }
     
    
  • controllers/resource.go+1 1 modified
    @@ -364,7 +364,7 @@ func (c *ApiController) UploadResource() {
     		}
     
     		applicationObj.TermsOfUse = fileUrl
    -		_, err = object.UpdateApplication(applicationId, applicationObj)
    +		_, err = object.UpdateApplication(applicationId, applicationObj, true)
     		if err != nil {
     			c.ResponseError(err.Error())
     			return
    
  • controllers/syncer.go+1 1 modified
    @@ -103,7 +103,7 @@ func (c *ApiController) UpdateSyncer() {
     		return
     	}
     
    -	c.Data["json"] = wrapActionResponse(object.UpdateSyncer(id, &syncer))
    +	c.Data["json"] = wrapActionResponse(object.UpdateSyncer(id, &syncer, c.IsGlobalAdmin()))
     	c.ServeJSON()
     }
     
    
  • controllers/token.go+1 1 modified
    @@ -105,7 +105,7 @@ func (c *ApiController) UpdateToken() {
     		return
     	}
     
    -	c.Data["json"] = wrapActionResponse(object.UpdateToken(id, &token))
    +	c.Data["json"] = wrapActionResponse(object.UpdateToken(id, &token, c.IsGlobalAdmin()))
     	c.ServeJSON()
     }
     
    
  • controllers/webhook.go+1 1 modified
    @@ -105,7 +105,7 @@ func (c *ApiController) UpdateWebhook() {
     		return
     	}
     
    -	c.Data["json"] = wrapActionResponse(object.UpdateWebhook(id, &webhook))
    +	c.Data["json"] = wrapActionResponse(object.UpdateWebhook(id, &webhook, c.IsGlobalAdmin()))
     	c.ServeJSON()
     }
     
    
  • object/application.go+6 2 modified
    @@ -640,13 +640,17 @@ func GetAllowedApplications(applications []*Application, userId string, lang str
     	return res, nil
     }
     
    -func UpdateApplication(id string, application *Application) (bool, error) {
    +func UpdateApplication(id string, application *Application, isGlobalAdmin bool) (bool, error) {
     	owner, name := util.GetOwnerAndNameFromId(id)
     	oldApplication, err := getApplication(owner, name)
     	if oldApplication == nil {
     		return false, err
     	}
     
    +	if !isGlobalAdmin && oldApplication.Organization != application.Organization {
    +		return false, fmt.Errorf("auth:Unauthorized operation")
    +	}
    +
     	if name == "app-built-in" {
     		application.Name = name
     	}
    @@ -723,7 +727,7 @@ func AddApplication(application *Application) (bool, error) {
     }
     
     func deleteApplication(application *Application) (bool, error) {
    -	affected, err := ormer.Engine.ID(core.PK{application.Owner, application.Name}).Delete(&Application{})
    +	affected, err := ormer.Engine.ID(core.PK{application.Owner, application.Name}).Where("organization = ?", application.Organization).Delete(&Application{})
     	if err != nil {
     		return false, err
     	}
    
  • object/syncer.go+4 2 modified
    @@ -153,13 +153,15 @@ func GetMaskedSyncers(syncers []*Syncer, errs ...error) ([]*Syncer, error) {
     	return syncers, nil
     }
     
    -func UpdateSyncer(id string, syncer *Syncer) (bool, error) {
    +func UpdateSyncer(id string, syncer *Syncer, isGlobalAdmin bool) (bool, error) {
     	owner, name := util.GetOwnerAndNameFromId(id)
     	s, err := getSyncer(owner, name)
     	if err != nil {
     		return false, err
     	} else if s == nil {
     		return false, nil
    +	} else if !isGlobalAdmin && s.Organization != syncer.Organization {
    +		return false, fmt.Errorf("auth:Unauthorized operation")
     	}
     
     	session := ormer.Engine.ID(core.PK{owner, name}).AllCols()
    @@ -218,7 +220,7 @@ func AddSyncer(syncer *Syncer) (bool, error) {
     }
     
     func DeleteSyncer(syncer *Syncer) (bool, error) {
    -	affected, err := ormer.Engine.ID(core.PK{syncer.Owner, syncer.Name}).Delete(&Syncer{})
    +	affected, err := ormer.Engine.ID(core.PK{syncer.Owner, syncer.Name}).Where("organization = ?", syncer.Organization).Delete(&Syncer{})
     	if err != nil {
     		return false, err
     	}
    
  • object/token.go+4 2 modified
    @@ -180,12 +180,14 @@ func (token *Token) popularHashes() {
     	}
     }
     
    -func UpdateToken(id string, token *Token) (bool, error) {
    +func UpdateToken(id string, token *Token, isGlobalAdmin bool) (bool, error) {
     	owner, name := util.GetOwnerAndNameFromId(id)
     	if t, err := getToken(owner, name); err != nil {
     		return false, err
     	} else if t == nil {
     		return false, nil
    +	} else if !isGlobalAdmin && t.Organization != token.Organization {
    +		return false, nil
     	}
     
     	token.popularHashes()
    @@ -210,7 +212,7 @@ func AddToken(token *Token) (bool, error) {
     }
     
     func DeleteToken(token *Token) (bool, error) {
    -	affected, err := ormer.Engine.ID(core.PK{token.Owner, token.Name}).Delete(&Token{})
    +	affected, err := ormer.Engine.ID(core.PK{token.Owner, token.Name}).Where("organization = ?", token.Organization).Delete(&Token{})
     	if err != nil {
     		return false, err
     	}
    
  • object/webhook.go+4 2 modified
    @@ -104,12 +104,14 @@ func GetWebhook(id string) (*Webhook, error) {
     	return getWebhook(owner, name)
     }
     
    -func UpdateWebhook(id string, webhook *Webhook) (bool, error) {
    +func UpdateWebhook(id string, webhook *Webhook, isGlobalAdmin bool) (bool, error) {
     	owner, name := util.GetOwnerAndNameFromId(id)
     	if w, err := getWebhook(owner, name); err != nil {
     		return false, err
     	} else if w == nil {
     		return false, nil
    +	} else if !isGlobalAdmin && w.Organization != webhook.Organization {
    +		return false, fmt.Errorf("auth:Unauthorized operation")
     	}
     
     	affected, err := ormer.Engine.ID(core.PK{owner, name}).AllCols().Update(webhook)
    @@ -130,7 +132,7 @@ func AddWebhook(webhook *Webhook) (bool, error) {
     }
     
     func DeleteWebhook(webhook *Webhook) (bool, error) {
    -	affected, err := ormer.Engine.ID(core.PK{webhook.Owner, webhook.Name}).Delete(&Webhook{})
    +	affected, err := ormer.Engine.ID(core.PK{webhook.Owner, webhook.Name}).Where("organization = ?", webhook.Organization).Delete(&Webhook{})
     	if err != nil {
     		return false, err
     	}
    
  • routers/authz_filter.go+10 0 modified
    @@ -32,6 +32,7 @@ type Object struct {
     	Name         string `json:"name"`
     	AccessKey    string `json:"accessKey"`
     	AccessSecret string `json:"accessSecret"`
    +	Organization string `json:"organization"`
     }
     
     func getUsername(ctx *context.Context) (username string) {
    @@ -110,6 +111,15 @@ func getObject(ctx *context.Context) (string, string, error) {
     			return "", "", nil
     		}
     
    +		if strings.HasSuffix(path, "-application") || strings.HasSuffix(path, "-token") ||
    +			strings.HasSuffix(path, "-syncer") || strings.HasSuffix(path, "-webhook") {
    +			return obj.Organization, obj.Name, nil
    +		}
    +
    +		if strings.HasSuffix(path, "-organization") {
    +			return obj.Name, obj.Name, nil
    +		}
    +
     		if path == "/api/delete-resource" {
     			tokens := strings.Split(obj.Name, "/")
     			if len(tokens) >= 5 {
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.