Fix insecure /tmp usage in local backend (#872)

Since /tmp is writable by everybody, a user could precreate
/tmp/woodpecker with 777 permissions, allowing them to modify the
pipeline while it is being run, or preventing the pipeline from running.

And since os.MkdirAll error code wasn't checked, the same attacker
could have precreated the directory where the pipeline is executed to
mess with the run, allowing code execution under the UID of the
agent (who has access to the toke, to communicate with the server, which
mean a attacker could inject a fake agent, steal credentials, etc)
This commit is contained in:
mscherer 2022-04-06 03:33:00 +02:00 committed by GitHub
parent 1fb8003294
commit c3788d943f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"encoding/base64" "encoding/base64"
"io" "io"
"io/ioutil"
"os" "os"
"os/exec" "os/exec"
"strings" "strings"
@ -13,8 +14,9 @@ import (
) )
type local struct { type local struct {
cmd *exec.Cmd cmd *exec.Cmd
output io.ReadCloser output io.ReadCloser
workingdir string
} }
// make sure local implements Engine // make sure local implements Engine
@ -34,7 +36,9 @@ func (e *local) IsAvailable() bool {
} }
func (e *local) Load() error { func (e *local) Load() error {
return nil dir, err := ioutil.TempDir("", "woodpecker-local-*")
e.workingdir = dir
return err
} }
// Setup the pipeline environment. // Setup the pipeline environment.
@ -60,7 +64,7 @@ func (e *local) Exec(ctx context.Context, proc *types.Step) error {
if proc.Image == defaultCloneImage { if proc.Image == defaultCloneImage {
// Default clone step // Default clone step
Command = append(Command, "CI_WORKSPACE=/tmp/woodpecker/"+proc.Environment["CI_REPO"]) Command = append(Command, "CI_WORKSPACE="+e.workingdir+"/"+proc.Environment["CI_REPO"])
Command = append(Command, "plugin-git") Command = append(Command, "plugin-git")
} else { } else {
// Use "image name" as run command // Use "image name" as run command
@ -78,12 +82,14 @@ func (e *local) Exec(ctx context.Context, proc *types.Step) error {
// Prepare working directory // Prepare working directory
if proc.Image == defaultCloneImage { if proc.Image == defaultCloneImage {
e.cmd.Dir = "/tmp/woodpecker/" + proc.Environment["CI_REPO_OWNER"] e.cmd.Dir = e.workingdir + "/" + proc.Environment["CI_REPO_OWNER"]
} else { } else {
e.cmd.Dir = "/tmp/woodpecker/" + proc.Environment["CI_REPO"] e.cmd.Dir = e.workingdir + "/" + proc.Environment["CI_REPO"]
}
err := os.MkdirAll(e.cmd.Dir, 0o700)
if err != nil {
return err
} }
_ = os.MkdirAll(e.cmd.Dir, 0o700)
// Get output and redirect Stderr to Stdout // Get output and redirect Stderr to Stdout
e.output, _ = e.cmd.StdoutPipe() e.output, _ = e.cmd.StdoutPipe()
e.cmd.Stderr = e.cmd.Stdout e.cmd.Stderr = e.cmd.Stdout