Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for privilege escalation vulnerability #6748

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
9 changes: 9 additions & 0 deletions internal/home/home.go
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,15 @@ func initWorkingDir(opts options) (err error) {
if opts.workDir != "" {
// If there is a custom config file, use it's directory as our working dir
Context.workDir = opts.workDir
} else if !execDirAvaliable() {
// If running as a service and from /usr/bin/ use /var/lib for working dir instead of
// /usr/bin/data
Context.workDir = "/var/lib/AdGuardHome"

// Create dir if it does not already exist
if err := os.MkdirAll(Context.workDir, 0755); err != nil {
go-compile marked this conversation as resolved.
Show resolved Hide resolved
return err
}
} else {
Context.workDir = filepath.Dir(execPath)
}
Expand Down
58 changes: 58 additions & 0 deletions internal/home/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"io/fs"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
Expand Down Expand Up @@ -304,6 +305,11 @@ func handleServiceStatusCommand(s service.Service) {

// handleServiceStatusCommand handles service "install" command
func handleServiceInstallCommand(s service.Service) {
// Set the binary's permissions and move to /usr/bin (if on linux)
go-compile marked this conversation as resolved.
Show resolved Hide resolved
if err := secureBinary(); err != nil {
log.Fatal(err)
}

err := svcAction(s, "install")
if err != nil {
log.Fatalf("service: executing action %q: %s", "install", err)
Expand Down Expand Up @@ -681,3 +687,55 @@ rc_bg=YES

rc_cmd $1
`

// secureBinary is used before service.Install(). This function protects AdGuardHome from
// privilege escalation vulnerabilities caused by writable files
func secureBinary() error {
switch runtime.GOOS {
go-compile marked this conversation as resolved.
Show resolved Hide resolved
case "windows":
// TODO: support windows service support securely
// Set file owner to admin/system and public permissions read-only
return errors.Error("you currently cannot install adguardhome as a service on window")
go-compile marked this conversation as resolved.
Show resolved Hide resolved
default:
return secureBinaryUnix()
}
}

func secureBinaryUnix() error {
go-compile marked this conversation as resolved.
Show resolved Hide resolved
// Installalation can only be completed with root privileges, so check and handle if not
if os.Getuid() != 0 {
return errors.Error("permission denied. Root privileges required")
}

// Get current file path
binary := os.Args[0]

// Change owner to root:root
err := os.Chown(binary, 0, 0)
if err != nil {
return err
}

// Set permissions to root(read,write,exec), group(read,exec), public(read)
// This combined with changing the owner make the file undeletable without root privlages
// UNLESS THE PARENT FOLDER IS WRITABLE!
if err := os.Chmod(binary, 0755); err != nil {
return err
}

// Move binary to the PATH in a folder which is read-only to non root users
if err := os.Rename(binary, "/usr/bin/AdGuardHome"); err != nil {
go-compile marked this conversation as resolved.
Show resolved Hide resolved
return err
}

return nil
}

// execDirAvaliable returns true if the executable's current folder is avaliable to be
// used as a workDir.
// If AdGuardHome is running as a service, it should not use the binary's location as a
// workDir, thus this function will return false.
func execDirAvaliable() bool {
// If installed in /usr/bin do not use /usr/bin/data to store files
return filepath.Dir(os.Args[0]) != "/usr/bin"
}
Loading