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

Added support for macOS, additional IP formats, and to drop privileges #47

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 39 additions & 2 deletions cmd/singularity-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import (
"fmt"
"log"
"net/http"
"os/user"
"runtime"
"strconv"
"syscall"
"time"

"github.com/nccgroup/singularity"
Expand All @@ -32,6 +35,8 @@ func (a *arrayPortFlags) Set(value string) error {
func initFromCmdLine() *singularity.AppConfig {
var appConfig = singularity.AppConfig{}
var myArrayPortFlags arrayPortFlags
var f = false
var enableLinuxTProxySupport = &f

var responseIPAddr = flag.String("ResponseIPAddr", "192.168.0.1",
"Specify the attacker host IP address that will be rebound to the victim host address using strategy specified by flag \"-DNSRebingStrategy\"")
Expand All @@ -42,7 +47,11 @@ func initFromCmdLine() *singularity.AppConfig {
var dangerouslyAllowDynamicHTTPServers = flag.Bool("dangerouslyAllowDynamicHTTPServers", false, "DANGEROUS if the flag is set (to anything). Specify if any target can dynamically request Singularity to allocate an HTTP Server on a new port.")
var WsHttpProxyServerPort = flag.Int("WsHttpProxyServerPort", 3129,
"Specify the attacker HTTP Proxy Server and Websockets port that permits to browse hijacked client services.")
var enableLinuxTProxySupport = flag.Bool("enableLinuxTProxySupport", false, "Specify whether to enable Linux TProxy support or not. Useful to listen on many ports with an appropriate iptables configuration.")
if runtime.GOOS != "darwin" {
enableLinuxTProxySupport = flag.Bool("enableLinuxTProxySupport", false, "Specify whether to enable Linux TProxy support or not. Useful to listen on many ports with an appropriate iptables configuration.")
}
var dontDropPrivileges = flag.Bool("dontDropPrivileges", false, "Don't drop privileges if running as the root user. By default privileges will be dropped to the 'nobody' user and group")
var dropToUserName = flag.String("dropToUserName", "nobody", "Specify the username of the account you would like to drop privileges to, defaults to 'nobody'")
flag.Var(&myArrayPortFlags, "HTTPServerPort", "Specify the attacker HTTP Server port that will serve HTML/JavaScript files. Repeat this flag to listen on more than one HTTP port.")
var dnsServerBindAddr = flag.String("DNSServerBindAddr", "0.0.0.0", "Specify the IP address the DNS server will bind to, defaults to 0.0.0.0")

Expand All @@ -65,6 +74,8 @@ func initFromCmdLine() *singularity.AppConfig {
appConfig.DNSServerBindAddr = *dnsServerBindAddr
appConfig.WsHTTPProxyServerPort = *WsHttpProxyServerPort
appConfig.EnableLinuxTProxySupport = *enableLinuxTProxySupport
appConfig.DontDropPrivileges = *dontDropPrivileges
appConfig.DropToUserName = *dropToUserName

return &appConfig
}
Expand Down Expand Up @@ -114,7 +125,6 @@ func main() {
if httpServerErr != nil {
log.Fatalf("Main: Could not start main HTTP Server instance: %v", httpServerErr)
}

}

wsHTTPProxyServer := singularity.NewHTTPProxyServer(hss.WsHTTPProxyServerPort, dcss, wscss, hss)
Expand All @@ -124,6 +134,33 @@ func main() {
log.Fatalf("Main: Could not start proxy Webssockets/HTTP Server instance: %v", wsHTTPProxyServerErr)
}

if syscall.Getuid() == 0 && appConfig.DontDropPrivileges == false {
log.Printf("Main: Running as root, dropping privileges to user %s\n", appConfig.DropToUserName)
dropToUser, err := user.Lookup(appConfig.DropToUserName)
if err != nil {
log.Fatalf("User not found: %s\n", err)
}

uid, err := strconv.ParseInt(dropToUser.Uid, 10, 64)
if err != nil {
log.Fatalf("Main: Could not parse UID for user %s, error: %s\n", appConfig.DropToUserName, err)
}

gid, err := strconv.ParseInt(dropToUser.Gid, 10, 64)
if err != nil {
log.Fatalf("Main: Could not parse GID for user %s\n", appConfig.DropToUserName)
}
if err := syscall.Setgid(int(gid)); err != nil {
log.Fatalf("Main: Error setting GID %d: %s\n", gid, err)
}

if err := syscall.Setuid(int(uid)); err != nil {
log.Fatalf("Main: Error setting UID %d: %s\n", uid, err)
}

log.Printf("Main: Running as %s, %d:%d\n", appConfig.DropToUserName, uid, gid)
}

expiryDuration := time.Duration(appConfig.ResponseReboundIPAddrtimeOut) * time.Second
expireClientStateTicker := time.NewTicker(expiryDuration)

Expand Down
51 changes: 32 additions & 19 deletions singularity.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package singularity
import (
"context"
crand "crypto/rand"
"encoding/binary"
"encoding/hex"
"encoding/json"
"errors"
Expand All @@ -15,10 +16,10 @@ import (
"net/http"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
"sync"
"syscall"
"time"

"github.com/miekg/dns"
Expand Down Expand Up @@ -56,6 +57,20 @@ type AppConfig struct {
DNSServerBindAddr string
WsHTTPProxyServerPort int
EnableLinuxTProxySupport bool
DontDropPrivileges bool
DropPrivilegesUserName bool
DropToUserName string
}

// Parse IP address from a string or 32-bit integer
func parseIP(s string) net.IP {
// parse ip and if decimal, convert to IP
if ipInt, err := strconv.ParseUint(s, 10, 32); err == nil {
ip := make(net.IP, 4)
binary.BigEndian.PutUint32(ip, uint32(ipInt))
return ip
}
return net.ParseIP(s)
}

// GenerateRandomString returns a secure random hexstring, 20 chars long
Expand Down Expand Up @@ -145,22 +160,24 @@ func NewDNSQuery(qname string) (*DNSQuery, error) {
return name, errors.New("cannot parse DNS query")
}

if net.ParseIP(elements[0]) == nil {
if parseIP(elements[0]) == nil {
return name, errors.New("cannot parse IP address of first host in DNS query")

}
name.ResponseIPAddr = elements[0]
name.ResponseIPAddr = parseIP(elements[0]).String()

if elements[1] != "localhost" {

elements[1] = strings.Replace(elements[1], "_", "-", -1)
if net.ParseIP(elements[1]) == nil && golang.IsDomainName(elements[1]) == false {
if parseIP(elements[1]) != nil {
name.ResponseReboundIPAddr = parseIP(elements[1]).String()
} else if golang.IsDomainName(elements[1]) != false {
name.ResponseReboundIPAddr = elements[1]
} else {
return name, errors.New("cannot parse IP address or CNAME of second host in DNS query")
}
} else {
name.ResponseReboundIPAddr = elements[1]
}

name.ResponseReboundIPAddr = elements[1]

name.Session = elements[2]

if len(name.Session) == 0 {
Expand Down Expand Up @@ -775,24 +792,20 @@ type HTTPServerError struct {
Port string
}

// Linux Transparent Proxy Support
// https://www.kernel.org/doc/Documentation/networking/tproxy.txt
// e.g. `sudo iptables -t mangle -I PREROUTING -d ext_ip_address
// -p tcp --dport 8080 -j TPROXY --on-port=80 --on-ip=ext_ip_address
// will redirect external port 8080 on port 80 of Singularity
func useIPTransparent(network, address string, conn syscall.RawConn) error {
return conn.Control(func(descriptor uintptr) {
syscall.SetsockoptInt(int(descriptor), syscall.IPPROTO_IP, syscall.IP_TRANSPARENT, 1)
})
}

// StartHTTPServer starts an HTTP server
// and adds it to dynamic (if dynamic is true) or static HTTP Store
func StartHTTPServer(s *http.Server, hss *HTTPServerStoreHandler, dynamic bool, tproxy bool) error {

var err error
var l net.Listener

if runtime.GOOS == "darwin" {
if tproxy == true {
log.Printf("HTTP: Transparent proxy support is not availible on macOS\n")
}
tproxy = false
}

if tproxy == true {
listenConfig := &net.ListenConfig{Control: useIPTransparent}
l, err = listenConfig.Listen(context.Background(), "tcp", s.Addr)
Expand Down
8 changes: 8 additions & 0 deletions singularity_darwin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package singularity

import "syscall"

// Currently not supported on macOS, this is a blank function to help compilation
func useIPTransparent(_, _ string, _ syscall.RawConn) error {
return nil
}
13 changes: 13 additions & 0 deletions singularity_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package singularity

// Linux Transparent Proxy Support
// https://www.kernel.org/doc/Documentation/networking/tproxy.txt
// e.g. `sudo iptables -t mangle -I PREROUTING -d ext_ip_address
// -p tcp --dport 8080 -j TPROXY --on-port=80 --on-ip=ext_ip_address
// will redirect external port 8080 on port 80 of Singularity

func useIPTransparent(network, address string, conn syscall.RawConn) error {
return conn.Control(func(descriptor uintptr) {
syscall.SetsockoptInt(int(descriptor), syscall.IPPROTO_IP, syscall.IP_TRANSPARENT, 1)
})
}