CVE-2023-37469: Improper Neutralization of Special Elements used in a Command ('Command Injection') (CWE-77)
value: CasaOS is an open-source personal cloud system. Prior to version 0.4.4, if an authenticated user using CasaOS is able to successfully connect to a controlled SMB server, they are able to execute arbitrary commands. Version 0.4.4 contains a patch for the issue.
CWE description: The product constructs all or part of a command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended command when it is sent to a downstream component.
CVSS v3 base score: 8.8
Published: 2023-08-24T23:15Z
Repository: https://github.com/IceWhaleTech/CasaOS
Language: Go
Files changed: route/v1/samba.go, service/connections.go
Commit date: 2023-04-24 11:24:22 +0800
Commit message: Update samba (#1021)


Diff: diff --git a/route/v1/samba.go b/route/v1/samba.go
index 80deb82..452f61d 100644
--- a/route/v1/samba.go
+++ b/route/v1/samba.go
@@ -14,22 +14,20 @@ import (
 	"fmt"
 	"io/ioutil"
 	"net/http"
 	"os"
 	"path/filepath"
-	"regexp"
 	"strings"
 
 	"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
 	"github.com/IceWhaleTech/CasaOS-Common/utils/systemctl"
 	"go.uber.org/zap"
 
 	"github.com/IceWhaleTech/CasaOS/model"
 	"github.com/IceWhaleTech/CasaOS/pkg/samba"
 	"github.com/IceWhaleTech/CasaOS/pkg/utils/common_err"
 	"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
-	"github.com/IceWhaleTech/CasaOS/pkg/utils/ip_helper"
 	"github.com/IceWhaleTech/CasaOS/service"
 	model2 "github.com/IceWhaleTech/CasaOS/service/model"
 	"github.com/gin-gonic/gin"
 )
 
@@ -137,26 +135,26 @@ func PostSambaConnectionsCreate(c *gin.Context) {
 	if connection.Username == "" || connection.Host == "" {
 		c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.CHARACTER_LIMIT, Message: common_err.GetMsg(common_err.CHARACTER_LIMIT)})
 		return
 	}
 
-	if ok, _ := regexp.MatchString(`^[\w@#*.]{4,30}$`, connection.Password); !ok {
-		c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.CHARACTER_LIMIT, Message: common_err.GetMsg(common_err.CHARACTER_LIMIT)})
-		return
-	}
-	if ok, _ := regexp.MatchString(`^[\w@#*.]{4,30}$`, connection.Username); !ok {
-		c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
-		return
-	}
-	if !ip_helper.IsIPv4(connection.Host) && !ip_helper.IsIPv6(connection.Host) {
-		c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
-		return
-	}
-	if ok, _ := regexp.MatchString("^[0-9]{1,6}$", connection.Port); !ok {
-		c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
-		return
-	}
+	// if ok, _ := regexp.MatchString(`^[\w@#*.]{4,30}$`, connection.Password); !ok {
+	// 	c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.CHARACTER_LIMIT, Message: common_err.GetMsg(common_err.CHARACTER_LIMIT)})
+	// 	return
+	// }
+	// if ok, _ := regexp.MatchString(`^[\w@#*.]{4,30}$`, connection.Username); !ok {
+	// 	c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
+	// 	return
+	// }
+	// if !ip_helper.IsIPv4(connection.Host) && !ip_helper.IsIPv6(connection.Host) {
+	// 	c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
+	// 	return
+	// }
+	// if ok, _ := regexp.MatchString("^[0-9]{1,6}$", connection.Port); !ok {
+	// 	c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
+	// 	return
+	// }
 
 	connection.Host = strings.Split(connection.Host, "/")[0]
 	// check is exists
 	connections := service.MyService.Connections().GetConnectionByHost(connection.Host)
 	if len(connections) > 0 {
diff --git a/service/connections.go b/service/connections.go
index b304bc0..bc35c79 100644
--- a/service/connections.go
+++ b/service/connections.go
@@ -9,26 +9,27 @@
  * Copyright (c) 2022 by icewhale, All Rights Reserved.
  */
 package service
 
 import (
-	"github.com/IceWhaleTech/CasaOS/pkg/config"
-	command2 "github.com/IceWhaleTech/CasaOS/pkg/utils/command"
+	"fmt"
+
 	"github.com/IceWhaleTech/CasaOS/service/model"
 	model2 "github.com/IceWhaleTech/CasaOS/service/model"
 	"github.com/moby/sys/mount"
+	"golang.org/x/sys/unix"
 	"gorm.io/gorm"
 )
 
 type ConnectionsService interface {
 	GetConnectionsList() (connections []model2.ConnectionsDBModel)
 	GetConnectionByHost(host string) (connections []model2.ConnectionsDBModel)
 	GetConnectionByID(id string) (connections model2.ConnectionsDBModel)
 	CreateConnection(connection *model2.ConnectionsDBModel)
 	DeleteConnection(id string)
 	UpdateConnection(connection *model2.ConnectionsDBModel)
-	MountSmaba(username, host, directory, port, mountPoint, password string) string
+	MountSmaba(username, host, directory, port, mountPoint, password string) error
 	UnmountSmaba(mountPoint string) error
 }
 
 type connectionsStruct struct {
 	db *gorm.DB
@@ -54,13 +55,21 @@ func (s *connectionsStruct) UpdateConnection(connection *model2.ConnectionsDBMod
 }
 func (s *connectionsStruct) DeleteConnection(id string) {
 	s.db.Where("id= ?", id).Delete(&model.ConnectionsDBModel{})
 }
 
-func (s *connectionsStruct) MountSmaba(username, host, directory, port, mountPoint, password string) string {
-	str := command2.ExecResultStr("source " + config.AppInfo.ShellPath + "/helper.sh ;MountCIFS " + username + " " + host + " " + directory + " " + port + " " + mountPoint + " " + password)
-	return str
+func (s *connectionsStruct) MountSmaba(username, host, directory, port, mountPoint, password string) error {
+	err := unix.Mount(
+		fmt.Sprintf("//%s/%s", host, directory),
+		mountPoint,
+		"cifs",
+		unix.MS_NOATIME|unix.MS_NODEV|unix.MS_NOSUID,
+		fmt.Sprintf("username=%s,password=%s", username, password),
+	)
+	return err
+	//str := command2.ExecResultStr("source " + config.AppInfo.ShellPath + "/helper.sh ;MountCIFS " + username + " " + host + " " + directory + " " + port + " " + mountPoint + " " + password)
+	//return str
 }
 func (s *connectionsStruct) UnmountSmaba(mountPoint string) error {
 	return mount.Unmount(mountPoint)
 }
 

Vulnerable code: 	"regexp"
	"github.com/IceWhaleTech/CasaOS/pkg/utils/ip_helper"
	if ok, _ := regexp.MatchString(`^[\w@#*.]{4,30}$`, connection.Password); !ok {
		c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.CHARACTER_LIMIT, Message: common_err.GetMsg(common_err.CHARACTER_LIMIT)})
		return
	}
	if ok, _ := regexp.MatchString(`^[\w@#*.]{4,30}$`, connection.Username); !ok {
		c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
		return
	}
	if !ip_helper.IsIPv4(connection.Host) && !ip_helper.IsIPv6(connection.Host) {
		c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
		return
	}
	if ok, _ := regexp.MatchString("^[0-9]{1,6}$", connection.Port); !ok {
		c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
		return
	}
	"github.com/IceWhaleTech/CasaOS/pkg/config"
	command2 "github.com/IceWhaleTech/CasaOS/pkg/utils/command"
	MountSmaba(username, host, directory, port, mountPoint, password string) string
func (s *connectionsStruct) MountSmaba(username, host, directory, port, mountPoint, password string) string {
	str := command2.ExecResultStr("source " + config.AppInfo.ShellPath + "/helper.sh ;MountCIFS " + username + " " + host + " " + directory + " " + port + " " + mountPoint + " " + password)
	return str
Fixed code: 	// if ok, _ := regexp.MatchString(`^[\w@#*.]{4,30}$`, connection.Password); !ok {
	// 	c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.CHARACTER_LIMIT, Message: common_err.GetMsg(common_err.CHARACTER_LIMIT)})
	// 	return
	// }
	// if ok, _ := regexp.MatchString(`^[\w@#*.]{4,30}$`, connection.Username); !ok {
	// 	c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
	// 	return
	// }
	// if !ip_helper.IsIPv4(connection.Host) && !ip_helper.IsIPv6(connection.Host) {
	// 	c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
	// 	return
	// }
	// if ok, _ := regexp.MatchString("^[0-9]{1,6}$", connection.Port); !ok {
	// 	c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
	// 	return
	// }
	"fmt"

	"golang.org/x/sys/unix"
	MountSmaba(username, host, directory, port, mountPoint, password string) error
func (s *connectionsStruct) MountSmaba(username, host, directory, port, mountPoint, password string) error {
	err := unix.Mount(
		fmt.Sprintf("//%s/%s", host, directory),
		mountPoint,
		"cifs",
		unix.MS_NOATIME|unix.MS_NODEV|unix.MS_NOSUID,
		fmt.Sprintf("username=%s,password=%s", username, password),
	)
	return err
	//str := command2.ExecResultStr("source " + config.AppInfo.ShellPath + "/helper.sh ;MountCIFS " + username + " " + host + " " + directory + " " + port + " " + mountPoint + " " + password)
	//return str