Skip to content
Privilege de-escalation listen in Go.
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.gitignore
LICENSE
README.md
example_listen_test.go
example_listenandserve_test.go
listen.go

README.md

golisten

Privilege de-escalation listen in Go.

Overview

golisten expects to be root. If it is not, it will error. As root, perform a net.Listen(). Once this is done, we can perform the privilege de-escalation. Because of the Go thread model, it is not safe to do so with a "simple" syscall.Setuid(). In order to ensure that the whole process (all threads) are de-escalated, golsiten will fork itself as the requested user while inheriting the privileged listened file descriptor.

golisten.ListenAndServe works just like http.ListenAndServe but expect the target user to be run as.

Example

ListenAndServe

package main

import (
	"fmt"
	"log"
	"net/http"
	"os/user"

	"github.com/creack/golisten"
)

func handler(w http.ResponseWriter, req *http.Request) {
	u, err := user.Current()
	if err != nil {
		log.Printf("Error getting user: %s", err)
		return
	}
	fmt.Fprintf(w, "%s\n", u.Uid)
}

func main() {
	http.HandleFunc("/", handler)
	log.Fatal(golisten.ListenAndServe("guillaume", ":80", nil))
}

Listen

package main

import (
	"fmt"
	"log"
	"net/http"
	"os/user"

	"github.com/creack/golisten"
)

func handler(w http.ResponseWriter, req *http.Request) {
	u, err := user.Current()
	if err != nil {
		log.Printf("Error getting user: %s", err)
		return
	}
	fmt.Fprintf(w, "%s\n", u.Uid)
}

func ExampleListenAndServe() {
	ln, err := golisten.Listen("guillaume", "tcp", ":80")
	if err != nil {
		log.Fatal(err)
	}
	http.HandleFunc("/", handler)
	log.Fatal(http.Serve(ln, nil))
}

TODO

  • ListenAndServeTLS
  • Benchmarks
  • Tests
  • Use environment instead of command-line flag for fd passing
You can’t perform that action at this time.