Security implications of a socket race when tunnelling a sub-command

115 views Asked by At

I want to tunnel a sub-command through a connection by listening to a port, running the sub-command (to connect to that port), and then forwarding the data through the connection:

package main

import (
    "fmt"
    "net"
    "os"
    "os/exec"
)

func main() {
    ln, err := net.ListenTCP("tcp4", &net.TCPAddr{IP: localhost})
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
    defer ln.Close()

    port := ln.Addr().(*net.TCPAddr).Port

    cmd := exec.Command(
        "git",
        "clone",
        fmt.Sprintf("git://127.0.0.1:%d/project.git", port),
    )

    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr

    if err := cmd.Start(); err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
    defer cmd.Process.Kill()

    errs := make(chan error, 1)
    go func() {
        errs <- cmd.Wait()
    }()

    conns := make(chan net.Conn, 1)
    go func() {
        conn, err := ln.Accept()
        if err == nil {
            conns <- conn
        } else {
            fmt.Println(err)
            errs <- err
        }
    }()

    select {
    case err := <-errs:
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    case conn := <-conns:
        defer conn.Close()
        // TODO Tunnel data from `conn` through another connection.
    }

    fmt.Println("done.")
}

var localhost = net.IPv4(127, 0, 0, 1)

However, there's a race here between the time that we start listening and the time when the sub-command actually connects to the listener, where another process can connect to the listener. I believe this race could be exploited by an attacker to communicate with the process at the other end of the connection and achieve results that would otherwise require privilege escalation to perform (example attacks that require special permissions are replacing the git command with a malicious program or simply reading the contents of the cloned directory, in this instance).

Should this be a concern? If so, is there a way it can be prevented? Though the question is asked using Go as an example, answers and comments in any language are welcome.

2

There are 2 answers

1
Warren Dew On

Yes it is a concern. It can be prevented by using some form of authentication so that your server only allows connections from legitimate clients.

0
Sean Kelleher On

Expanding on Warren Dew's answer, assuming the other end of the tunnel connects to an SSH server then this will perform authentication of the Git server/client.

A more general approach to "add" authentication to a sub-command for this purpose is to wrap the sub-command in a container such as docker and have the sub-command tunnel through a connection that authenticates itself. Though the tunnel's race condition is still present, it is at a higher "level" (i.e. inside the container), meaning that an attacker with no privileges will not be able to exploit the race condition in the base system. The downside to this approach is its inherent complexity and the overhead (though minimal) of running the sub-command in a container.