Command injection in firewall ip functionality in 1Panel
Description
1Panel is an open source Linux server operation and maintenance management panel. An OS command injection vulnerability exists in 1Panel firewall functionality. A specially-crafted HTTP request can lead to arbitrary command execution. An attacker can make an authenticated HTTP request to trigger this vulnerability. 1Panel firewall functionality /hosts/firewall/ip endpoint read user input without validation, the attacker extends the default functionality of the application, which execute system commands. An attacker can execute arbitrary code on the target system, which can lead to a complete compromise of the system. This issue has been addressed in commit e17b80cff49 which is included in release version 1.4.3. Users are advised to upgrade. There are no known workarounds for this vulnerability.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/1Panel-dev/1PanelGo | < 1.4.3 | 1.4.3 |
Affected products
1- Range: < 1.4.3
Patches
1e17b80cff497fix: 解决部分接口命令注入问题 (#1690)
12 files changed · +58 −7
backend/app/service/container_compose.go+14 −0 modified@@ -14,8 +14,10 @@ import ( "github.com/1Panel-dev/1Panel/backend/app/dto" "github.com/1Panel-dev/1Panel/backend/app/model" + "github.com/1Panel-dev/1Panel/backend/buserr" "github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/global" + "github.com/1Panel-dev/1Panel/backend/utils/cmd" "github.com/1Panel-dev/1Panel/backend/utils/compose" "github.com/1Panel-dev/1Panel/backend/utils/docker" "github.com/docker/docker/api/types" @@ -127,6 +129,9 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface } func (u *ContainerService) TestCompose(req dto.ComposeCreate) (bool, error) { + if cmd.CheckIllegal(req.Path) { + return false, buserr.New(constant.ErrCmdIllegal) + } composeItem, _ := composeRepo.GetRecord(commonRepo.WithByName(req.Name)) if composeItem.ID != 0 { return false, constant.ErrRecordExist @@ -143,6 +148,9 @@ func (u *ContainerService) TestCompose(req dto.ComposeCreate) (bool, error) { } func (u *ContainerService) CreateCompose(req dto.ComposeCreate) (string, error) { + if cmd.CheckIllegal(req.Name, req.Path) { + return "", buserr.New(constant.ErrCmdIllegal) + } if err := u.loadPath(&req); err != nil { return "", err } @@ -177,6 +185,9 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) (string, error) } func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error { + if cmd.CheckIllegal(req.Path, req.Operation) { + return buserr.New(constant.ErrCmdIllegal) + } if _, err := os.Stat(req.Path); err != nil { return fmt.Errorf("load file with path %s failed, %v", req.Path, err) } @@ -195,6 +206,9 @@ func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error { } func (u *ContainerService) ComposeUpdate(req dto.ComposeUpdate) error { + if cmd.CheckIllegal(req.Name, req.Path) { + return buserr.New(constant.ErrCmdIllegal) + } if _, err := os.Stat(req.Path); err != nil { return fmt.Errorf("load file with path %s failed, %v", req.Path, err) }
backend/app/service/container.go+4 −0 modified@@ -18,6 +18,7 @@ import ( "github.com/1Panel-dev/1Panel/backend/buserr" "github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/global" + "github.com/1Panel-dev/1Panel/backend/utils/cmd" "github.com/1Panel-dev/1Panel/backend/utils/common" "github.com/1Panel-dev/1Panel/backend/utils/docker" "github.com/docker/docker/api/types" @@ -552,6 +553,9 @@ func (u *ContainerService) ContainerLogClean(req dto.OperationWithName) error { } func (u *ContainerService) ContainerLogs(wsConn *websocket.Conn, container, since, tail string, follow bool) error { + if cmd.CheckIllegal(container, since, tail) { + return buserr.New(constant.ErrCmdIllegal) + } command := fmt.Sprintf("docker logs %s", container) if tail != "0" { command += " -n " + tail
backend/app/service/database_mysql.go+12 −0 modified@@ -17,6 +17,7 @@ import ( "github.com/1Panel-dev/1Panel/backend/buserr" "github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/global" + "github.com/1Panel-dev/1Panel/backend/utils/cmd" "github.com/1Panel-dev/1Panel/backend/utils/common" "github.com/1Panel-dev/1Panel/backend/utils/compose" _ "github.com/go-sql-driver/mysql" @@ -77,6 +78,10 @@ var formatMap = map[string]string{ } func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*model.DatabaseMysql, error) { + if cmd.CheckIllegal(req.Name, req.Username, req.Password, req.Format, req.Permission) { + return nil, buserr.New(constant.ErrCmdIllegal) + } + if req.Username == "root" { return nil, errors.New("Cannot set root as user name") } @@ -184,6 +189,10 @@ func (u *MysqlService) Delete(ctx context.Context, req dto.MysqlDBDelete) error } func (u *MysqlService) ChangePassword(info dto.ChangeDBInfo) error { + if cmd.CheckIllegal(info.Value) { + return buserr.New(constant.ErrCmdIllegal) + } + var ( mysql model.DatabaseMysql err error @@ -253,6 +262,9 @@ func (u *MysqlService) ChangePassword(info dto.ChangeDBInfo) error { } func (u *MysqlService) ChangeAccess(info dto.ChangeDBInfo) error { + if cmd.CheckIllegal(info.Value) { + return buserr.New(constant.ErrCmdIllegal) + } var ( mysql model.DatabaseMysql err error
backend/app/service/firewall.go+0 −1 modified@@ -304,7 +304,6 @@ func OperateFirewallPort(oldPorts, newPorts []int) error { return err } for _, port := range newPorts { - if err := client.Port(fireClient.FireInfo{Port: strconv.Itoa(port), Protocol: "tcp", Strategy: "accept"}, "add"); err != nil { return err }
backend/app/service/image_repo.go+2 −2 modified@@ -79,7 +79,7 @@ func (u *ImageRepoService) List() ([]dto.ImageRepoOption, error) { func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error { if cmd.CheckIllegal(req.Username, req.Password, req.DownloadUrl) { - return buserr.New(constant.ErrRepoConn) + return buserr.New(constant.ErrCmdIllegal) } imageRepo, _ := imageRepoRepo.Get(commonRepo.WithByName(req.Name)) if imageRepo.ID != 0 { @@ -148,7 +148,7 @@ func (u *ImageRepoService) Update(req dto.ImageRepoUpdate) error { return errors.New("The default value cannot be deleted !") } if cmd.CheckIllegal(req.Username, req.Password, req.DownloadUrl) { - return buserr.New(constant.ErrRepoConn) + return buserr.New(constant.ErrCmdIllegal) } repo, err := imageRepoRepo.Get(commonRepo.WithByID(req.ID)) if err != nil {
backend/app/service/ssh.go+4 −0 modified@@ -11,6 +11,7 @@ import ( "time" "github.com/1Panel-dev/1Panel/backend/app/dto" + "github.com/1Panel-dev/1Panel/backend/buserr" "github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/utils/cmd" @@ -146,6 +147,9 @@ func (u *SSHService) UpdateByFile(value string) error { } func (u *SSHService) GenerateSSH(req dto.GenerateSSH) error { + if cmd.CheckIllegal(req.EncryptionMode, req.Password) { + return buserr.New(constant.ErrCmdIllegal) + } currentUser, err := user.Current() if err != nil { return fmt.Errorf("load current user failed, err: %v", err)
backend/constant/errs.go+1 −1 modified@@ -42,6 +42,7 @@ var ( ErrTypePasswordExpired = "ErrPasswordExpired" ErrNameIsExist = "ErrNameIsExist" ErrDemoEnvironment = "ErrDemoEnvironment" + ErrCmdIllegal = "ErrCmdIllegal" ) // app @@ -107,7 +108,6 @@ var ( ErrInUsed = "ErrInUsed" ErrObjectInUsed = "ErrObjectInUsed" ErrPortRules = "ErrPortRules" - ErrRepoConn = "ErrRepoConn" ) // runtime
backend/i18n/lang/en.yaml+1 −1 modified@@ -13,6 +13,7 @@ ErrNotSupportType: "The system does not support the current type: {{ .detail }}" ErrNameIsExist: "Name is already exist" ErrDemoEnvironment: "Demo server, prohibit this operation!" ErrCmdTimeout: "Command execution timed out!" +ErrCmdIllegal: "The command contains illegal characters. Please modify and try again!" #app ErrPortInUsed: "{{ .detail }} port already in use" @@ -83,7 +84,6 @@ ErrTypeOfRedis: "The recovery file type does not match the current persistence m #container ErrInUsed: "{{ .detail }} is in use and cannot be deleted" ErrObjectInUsed: "This object is in use and cannot be deleted" -ErrRepoConn: "The repository information contains illegal characters" ErrPortRules: "The number of ports does not match, please re-enter!" #runtime
backend/i18n/lang/zh-Hant.yaml+1 −1 modified@@ -13,6 +13,7 @@ ErrNotSupportType: "系統暫不支持當前類型: {{ .detail }}" ErrNameIsExist: "名稱已存在" ErrDemoEnvironment: "演示伺服器,禁止此操作!" ErrCmdTimeout: "指令執行超時!" +ErrCmdIllegal: "執行命令中存在不合法字符,請修改後重試!" #app ErrPortInUsed: "{{ .detail }} 端口已被佔用!" @@ -83,7 +84,6 @@ ErrTypeOfRedis: "恢復文件類型與當前持久化方式不符,請修改後 #container ErrInUsed: "{{ .detail }} 正被使用,無法刪除" ErrObjectInUsed: "該對象正被使用,無法刪除" -ErrRepoConn: "倉庫資訊中存在不合法的字符" ErrPortRules: "端口數目不匹配,請重新輸入!" #runtime
backend/i18n/lang/zh.yaml+1 −1 modified@@ -13,6 +13,7 @@ ErrNotSupportType: "系统暂不支持当前类型: {{ .detail }}" ErrNameIsExist: "名称已存在" ErrDemoEnvironment: "演示服务器,禁止此操作!" ErrCmdTimeout: "命令执行超时!" +ErrCmdIllegal: "执行命令中存在不合法字符,请修改后重试!" #app ErrPortInUsed: "{{ .detail }} 端口已被占用!" @@ -83,7 +84,6 @@ ErrTypeOfRedis: "恢复文件类型与当前持久化方式不符,请修改后 #container ErrInUsed: "{{ .detail }} 正被使用,无法删除" ErrObjectInUsed: "该对象正被使用,无法删除" -ErrRepoConn: "仓库信息中存在不合法的字符" ErrPortRules: "端口数目不匹配,请重新输入!" #runtime
backend/utils/firewall/client/firewalld.go+9 −0 modified@@ -4,6 +4,8 @@ import ( "fmt" "strings" + "github.com/1Panel-dev/1Panel/backend/buserr" + "github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/utils/cmd" ) @@ -114,6 +116,10 @@ func (f *Firewall) ListAddress() ([]FireInfo, error) { } func (f *Firewall) Port(port FireInfo, operation string) error { + if cmd.CheckIllegal(operation, port.Protocol, port.Port) { + return buserr.New(constant.ErrCmdIllegal) + } + stdout, err := cmd.Execf("firewall-cmd --zone=public --%s-port=%s/%s --permanent", operation, port.Port, port.Protocol) if err != nil { return fmt.Errorf("%s port failed, err: %s", operation, stdout) @@ -122,6 +128,9 @@ func (f *Firewall) Port(port FireInfo, operation string) error { } func (f *Firewall) RichRules(rule FireInfo, operation string) error { + if cmd.CheckIllegal(operation, rule.Address, rule.Protocol, rule.Port, rule.Strategy) { + return buserr.New(constant.ErrCmdIllegal) + } ruleStr := "" if strings.Contains(rule.Address, "-") { std, err := cmd.Execf("firewall-cmd --permanent --new-ipset=%s --type=hash:ip", rule.Address)
backend/utils/firewall/client/ufw.go+9 −0 modified@@ -4,6 +4,8 @@ import ( "fmt" "strings" + "github.com/1Panel-dev/1Panel/backend/buserr" + "github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/utils/cmd" ) @@ -131,6 +133,9 @@ func (f *Ufw) Port(port FireInfo, operation string) error { default: return fmt.Errorf("unsupport strategy %s", port.Strategy) } + if cmd.CheckIllegal(port.Protocol, port.Port) { + return buserr.New(constant.ErrCmdIllegal) + } command := fmt.Sprintf("%s %s %s", f.CmdStr, port.Strategy, port.Port) if operation == "remove" { @@ -156,6 +161,10 @@ func (f *Ufw) RichRules(rule FireInfo, operation string) error { return fmt.Errorf("unsupport strategy %s", rule.Strategy) } + if cmd.CheckIllegal(operation, rule.Protocol, rule.Address, rule.Port) { + return buserr.New(constant.ErrCmdIllegal) + } + ruleStr := fmt.Sprintf("%s %s ", f.CmdStr, rule.Strategy) if operation == "remove" { ruleStr = fmt.Sprintf("%s delete %s ", f.CmdStr, rule.Strategy)
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
5- github.com/advisories/GHSA-p9xf-74xh-mhw5ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2023-37477ghsaADVISORY
- github.com/1Panel-dev/1Panel/commit/e17b80cff4975ee343568ff526b62319f499005dghsax_refsource_MISCWEB
- github.com/1Panel-dev/1Panel/releases/tag/v1.4.3ghsaWEB
- github.com/1Panel-dev/1Panel/security/advisories/GHSA-p9xf-74xh-mhw5ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.