8 mei 2019

Taken:

  1. Webserver: webhosting
  2. Webserver: urd2
  3. PaQu
    • Meer van LassyLarge gebruiken?

suid

Werkt met binary’s.

Werkt met scripts voor dash (met optie -p), python, python3, perl, php-cgi. Werkt niet met bash.

Dingen om op te letten:

  1. als uid == euid && gid == egid dan OK
  2. anders als uid != 33 || gid != 33 dan fout
  3. anders als $PATH != /usr/local/bin:/usr/bin:/bin dan fout
  4. anders als huidige directory niet gelijk is aan die van het programma, dan fout
  5. anders OK, maar wantrouw environment, stdin, command-line arguments

Voor scripts:

In .htaccess:

<FilesMatch ".+\.(sh|dash|py|py2|py3|pl|php)$">
    SetHandler my-script
</FilesMatch>
Action my-script /kleiweg/bin/myscript

myscript.go:

package main

import (
    "os"
    "os/exec"
    "path/filepath"
    "strings"
    "syscall"
)

var (
    host    = "urd2"
    userdir = "/net/homepages/kleiweg/www/"

    interpreters = map[string]string{
        // ".bash": "/bin/bash",
        ".sh":   "/bin/dash -p",
        ".dash": "/bin/dash -p",
        ".py":   "/usr/bin/python3",
        ".py2":  "/usr/bin/python",
        ".py3":  "/usr/bin/python3",
        ".pl":   "/usr/bin/perl",
        ".php":  "/usr/bin/php-cgi",
    }

    environ = []string{
        "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
        "TZ=Europe/Amsterdam",
    }

    unsafeNames = map[string]bool{
        "HTTP_PROXY": true,
    }

    varnames = []string{
        "AUTH_TYPE",
        "CONTENT_LENGTH",
        "CONTENT_TYPE",
        "CONTEXT_DOCUMENT_ROOT",
        "CONTEXT_PREFIX",
        "DATE_GMT",
        "DATE_LOCAL",
        "DOCUMENT_ARGS",
        "DOCUMENT_NAME",
        "DOCUMENT_PATH_INFO",
        "DOCUMENT_ROOT",
        "DOCUMENT_URI",
        "GATEWAY_INTERFACE",
        "HTTPS",
        "LAST_MODIFIED",
        "PATH_INFO",
        "PATH_TRANSLATED",
        "QUERY_STRING",
        "QUERY_STRING_UNESCAPED",
        "REMOTE_ADDR",
        "REMOTE_HOST",
        "REMOTE_IDENT",
        "REMOTE_PORT",
        "REMOTE_USER",
        "REDIRECT_ERROR_NOTES",
        "REDIRECT_HANDLER",
        "REDIRECT_QUERY_STRING",
        "REDIRECT_REMOTE_USER",
        "REDIRECT_SCRIPT_FILENAME",
        "REDIRECT_STATUS",
        "REDIRECT_URL",
        "REQUEST_METHOD",
        "REQUEST_URI",
        "REQUEST_SCHEME",
        "SCRIPT_FILENAME",
        "SCRIPT_NAME",
        "SCRIPT_URI",
        "SCRIPT_URL",
        "SERVER_ADMIN",
        "SERVER_NAME",
        "SERVER_ADDR",
        "SERVER_PORT",
        "SERVER_PROTOCOL",
        "SERVER_SIGNATURE",
        "SERVER_SOFTWARE",
        "UNIQUE_ID",
        "USER_NAME",
    }
)

func main() {

    // script met compleet path
    pathTranslated := os.Getenv("PATH_TRANSLATED")

    // is er geknoeid met het path van het script?
    if pathTranslated != filepath.Clean(pathTranslated) {
        return
    }

    // alleen op de juiste plek
    if h, _ := os.Hostname(); h != host || !strings.HasPrefix(pathTranslated, userdir) {
        return
    }

    // alleen als originele user:group == www-data:www-data
    if os.Getuid() != 33 || os.Getgid() != 33 {
        return
    }

    // alleen eigen scripts
    fi1, err1 := os.Stat(os.Args[0])
    fi2, err2 := os.Stat(pathTranslated)
    if err1 != nil || err2 != nil || fi1.Sys().(*syscall.Stat_t).Uid != fi2.Sys().(*syscall.Stat_t).Uid {
        return
    }

    // wat voor script?
    i := strings.LastIndex(pathTranslated, ".")
    if i < 0 {
        return
    }
    interpreter, ok := interpreters[pathTranslated[i:]]
    if !ok {
        return
    }

    // script kan in andere directory staan
    if os.Chdir(filepath.Dir(pathTranslated)) != nil {
        return
    }

    // maak een veilige environment
    for _, name := range varnames {
        if value := os.Getenv(name); value != "" {
            environ = append(environ, name+"="+value)
        }
    }
    seen := make(map[string]bool)
    for _, s := range os.Environ() {
        if strings.HasPrefix(s, "HTTP_") || strings.HasPrefix(s, "SSL_") {
            if a := strings.SplitN(s, "=", 2); len(a) == 2 && a[1] != "" {
                if seen[a[0]] || unsafeNames[a[0]] {
                    continue
                }
                environ = append(environ, s)
                seen[a[0]] = true
            }
        }
    }

    ii := strings.Fields(interpreter)
    args := ii[1:]
    args = append(args, pathTranslated)
    cmd := exec.Command(ii[0], args...)
    cmd.Env = environ
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    cmd.Run()

}

build:

go build myscript.go
chmod +s myscript

Foutmeldingen in /var/log/apache2/suexec.log:

OK:

[2019-05-06 15:14:05]: uid: (10107437/gosse) gid: (10107437/p107437) cmd: verwant_twnc.py

Fout 1:

[2019-05-06 15:22:24]: uid: (10107437/p107437) gid: (10107437/p107437) cmd: verwant_twnc.py
[2019-05-06 15:22:24]: cannot get docroot information (/home/p107437)

Fout 2:

[2019-05-06 16:08:55]: uid: (1006/gosse) gid: (1006/ar) cmd: verwant_twnc.py
[2019-05-06 16:08:55]: target uid/gid (1006/1006) mismatch with directory (10107437/10107437) or program (10107437/10107437)
urd webhosting