Skip to content

Instantly share code, notes, and snippets.

@leonjza
Last active January 12, 2021 23:28
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save leonjza/6c21e7b78b055e6b741809cc3f682374 to your computer and use it in GitHub Desktop.
Save leonjza/6c21e7b78b055e6b741809cc3f682374 to your computer and use it in GitHub Desktop.
☄️go-out - A dependency free, Golang egress buster using @mubix letmeoutofyour.net and @bhinfosecurity allports.exposed services.

☄️ go-out

This code now lives at: https://github.com/sensepost/go-out

A simple, dependency free, Golang egress buster using @mubix letmeoutofyour.net and @bhinfosecurity allports.exposed services.

install

Save the main.go file and either go run main.go or build it with go build -o go-out main.go, moving the resultant binary to your place of choice.

cross compiling

Build go-out for other platforms with:

GOOS=darwin GOARCH=amd64 go build -ldflags="-s -w" -o 'go-out-darwin-amd64' main.go
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o 'go-out-linux-amd64' main.go
GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" -o 'go-out-windows-amd64.exe' main.go

sample usage

CLI flags:

Usage of go-out:
  -end int
    	The end port to use. (default 65535)
  -https
    	Egress bust using HTTPs (letmeout only) (default true)
  -service string
    	Use 'letmeout' or 'allports' for this run. (default "letmeout")
  -start int
    	The start port to use. (default 1)
  -throttle
    	Throttle request speed. (random times, max 10sec) (default true)
  -w int
    	Number of concurrent workers to spawn. (default 5)

Sample run:

$ go-out -start=50 -end=80
===== Configuration =====
Service:	letmeout
Start Port:	50
End Port:	80
Workers:	5
HTTPS On:	true
Throttle:	false
=========================

[!] Egress on port 50
[!] Egress on port 54
[!] Egress on port 51
[!] Egress on port 53
[!] Egress on port 80
Done in 48.542120002s

contact

@leonjza

// go-out
//
// egress busting using:
// letmeoutofyour.net by @mubix
// allports.exposed by @bhinfosecurity
//
// 2018 @leonjza
package main
import (
"flag"
"fmt"
"io/ioutil"
"math/rand"
"net/http"
"net/url"
"strconv"
"strings"
"sync"
"time"
)
var (
servicePtr *string
startPortPtr *int
endPortPtr *int
concurrentPtr *int
useHTTPSPtr *bool
throttlePtr *bool
)
type service struct {
url string
match string
}
var services = map[string]service{
"letmeout": service{url: "go-out.letmeoutofyour.net", match: "w00tw00t"},
"allports": service{url: "allports.exposed", match: "<p>Open Port</p>"},
}
// maxedWaitGroup is a type to control the maximum
// number of goroutines in a wait group
type maxedWaitGroup struct {
current chan int
wg sync.WaitGroup
}
func (m *maxedWaitGroup) Add() {
m.current <- 1
m.wg.Add(1)
}
func (m *maxedWaitGroup) Done() {
<-m.current
m.wg.Done()
}
func (m *maxedWaitGroup) Wait() {
m.wg.Wait()
}
// validService ensures that we got a valid service from the
// -service commandline flag.
func validService(s *string) bool {
for b := range services {
if b == *s {
return true
}
}
return false
}
// validPort checks that we got a valid port from one of the
// port commandline flags.
func validPort(p int) bool {
if p > 0 && p <= 65535 {
return true
}
return false
}
// testHTTPEgress tests if a specific port is allowed to connect
// to the internet via http by matching the specific services' matcher
func (service *service) testHTTPEgress(port int) {
var scheme string
if *useHTTPSPtr {
scheme = "https://"
} else {
scheme = "http://"
}
url, err := url.Parse(scheme + service.url + ":" + strconv.Itoa(port))
if err != nil {
panic(err)
}
timeout := time.Duration(5 * time.Second)
client := http.Client{
Timeout: timeout,
}
resp, err := client.Get(url.String())
if err != nil {
// fmt.Printf("No connection on port %d\n", port)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
if strings.Contains(string(body), service.match) {
fmt.Printf("[!] Egress on port %d\n", port)
}
}
func validateFlags() bool {
// Flag Validation
if !validService(servicePtr) {
fmt.Printf("%s is an invalid service. Please choose 'letmeout' or 'allports'\n", *servicePtr)
return false
}
if *useHTTPSPtr && *servicePtr != "letmeout" {
fmt.Println("Only the 'letmeout' service supports HTTPS, disabling HTTPS checking.")
*useHTTPSPtr = false
}
if !validPort(*startPortPtr) || !validPort(*endPortPtr) {
fmt.Println("Either the start port or end port was invalid / out of range.")
return false
}
if *endPortPtr < *startPortPtr {
fmt.Println("End port should be larger than the start port.")
return false
}
return true
}
func main() {
servicePtr = flag.String("service", "letmeout", "Use 'letmeout' or 'allports' for this run.")
startPortPtr = flag.Int("start", 1, "The start port to use.")
endPortPtr = flag.Int("end", 65535, "The end port to use.")
concurrentPtr = flag.Int("w", 5, "Number of concurrent workers to spawn.")
useHTTPSPtr = flag.Bool("https", true, "Egress bust using HTTPs (letmeout only)")
throttlePtr = flag.Bool("throttle", false, "Throttle request speed. (random for a max of 10sec)")
flag.Parse()
if !validateFlags() {
return
}
fmt.Println("===== Configuration =====")
fmt.Printf("Service: %s\n", *servicePtr)
fmt.Printf("Start Port: %d\n", *startPortPtr)
fmt.Printf("End Port: %d\n", *endPortPtr)
fmt.Printf("Workers: %d\n", *concurrentPtr)
fmt.Printf("HTTPS On: %t\n", *useHTTPSPtr)
fmt.Printf("Throttle: %t\n", *throttlePtr)
fmt.Printf("=========================\n\n")
tester := services[*servicePtr]
start := time.Now()
mwg := maxedWaitGroup{
current: make(chan int, *concurrentPtr),
wg: sync.WaitGroup{},
}
// Process the ports in the range we got
for port := *startPortPtr; port <= *endPortPtr; port++ {
mwg.Add()
go func(p int) {
defer mwg.Done()
if *throttlePtr {
time.Sleep(time.Second * time.Duration(rand.Intn(10)))
}
tester.testHTTPEgress(p)
}(port)
}
// Wait for the work to complete
mwg.Wait()
fmt.Printf("Done in %s\n", time.Since(start))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment