mirror of
https://github.com/phishingclub/phishingclub.git
synced 2026-10-09 16:53:59 +02:00
156 lines
3.1 KiB
Go
156 lines
3.1 KiB
Go
//go:generate go run ./cmd/pack
|
|
|
|
package leakless
|
|
|
|
import (
|
|
"bytes"
|
|
"compress/gzip"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"math/rand"
|
|
"net"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"runtime"
|
|
"time"
|
|
|
|
"github.com/ysmood/leakless/pkg/shared"
|
|
"github.com/ysmood/leakless/pkg/utils"
|
|
)
|
|
|
|
var leaklessBinaries = map[string]string{}
|
|
|
|
// Launcher struct
|
|
type Launcher struct {
|
|
// Lock for leakless.LockPort, default is 2978
|
|
Lock int
|
|
|
|
pid chan int
|
|
err string
|
|
}
|
|
|
|
// New leakless instance
|
|
func New() *Launcher {
|
|
return &Launcher{
|
|
Lock: 2978,
|
|
pid: make(chan int),
|
|
}
|
|
}
|
|
|
|
// Command will try to download the leakless bin and prefix the exec.Cmd with the leakless options.
|
|
func (l *Launcher) Command(name string, arg ...string) *exec.Cmd {
|
|
bin := ""
|
|
func() {
|
|
defer LockPort(l.Lock)()
|
|
bin = GetLeaklessBin()
|
|
}()
|
|
|
|
uid := fmt.Sprintf("%x", utils.RandBytes(16))
|
|
addr := l.serve(uid)
|
|
|
|
arg = append([]string{uid, addr, name}, arg...)
|
|
return exec.Command(bin, arg...)
|
|
}
|
|
|
|
// Pid signals the pid of the guarded sub-process. The channel may never receive the pid.
|
|
func (l *Launcher) Pid() chan int {
|
|
return l.pid
|
|
}
|
|
|
|
// Err message from the guard process
|
|
func (l *Launcher) Err() string {
|
|
return l.err
|
|
}
|
|
|
|
func (l *Launcher) serve(uid string) string {
|
|
srv, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
panic("[leakless] serve error: " + err.Error())
|
|
}
|
|
|
|
go func() {
|
|
defer func() { _ = srv.Close() }()
|
|
|
|
conn, err := srv.Accept()
|
|
if err != nil {
|
|
l.err = err.Error()
|
|
l.pid <- 0
|
|
return
|
|
}
|
|
|
|
enc := json.NewEncoder(conn)
|
|
err = enc.Encode(shared.Message{UID: uid})
|
|
if err != nil {
|
|
l.err = err.Error()
|
|
l.pid <- 0
|
|
return
|
|
}
|
|
|
|
dec := json.NewDecoder(conn)
|
|
var msg shared.Message
|
|
err = dec.Decode(&msg)
|
|
if err == nil {
|
|
l.err = msg.Error
|
|
l.pid <- msg.PID
|
|
}
|
|
_ = dec.Decode(&msg)
|
|
}()
|
|
|
|
return srv.Addr().String()
|
|
}
|
|
|
|
var leaklessDir = filepath.Join(os.TempDir(), fmt.Sprintf("leakless-%s-%s", runtime.GOARCH, shared.Version))
|
|
|
|
// GetLeaklessBin returns the executable path of the guard, if it doesn't exists create one.
|
|
func GetLeaklessBin() string {
|
|
bin := filepath.Join(leaklessDir, "leakless")
|
|
|
|
if runtime.GOOS == "windows" {
|
|
bin += ".exe"
|
|
}
|
|
|
|
if !utils.FileExists(bin) {
|
|
name := utils.GetTarget().BinName()
|
|
raw, err := base64.StdEncoding.DecodeString(leaklessBinaries[name])
|
|
utils.E(err)
|
|
gr, err := gzip.NewReader(bytes.NewBuffer(raw))
|
|
utils.E(err)
|
|
data, err := ioutil.ReadAll(gr)
|
|
utils.E(err)
|
|
utils.E(gr.Close())
|
|
|
|
err = utils.OutputFile(bin, data, nil)
|
|
utils.E(err)
|
|
utils.E(os.Chmod(bin, 0755))
|
|
}
|
|
|
|
return bin
|
|
}
|
|
|
|
// Support returns true if the OS is supported by leakless.
|
|
func Support() bool {
|
|
_, has := leaklessBinaries[utils.GetTarget().BinName()]
|
|
return has
|
|
}
|
|
|
|
// LockPort uses a tcp port to create a mutex lock for cross-process locking.
|
|
// It will poll the port to check if it's free.
|
|
func LockPort(port int) func() {
|
|
var l net.Listener
|
|
for {
|
|
var err error
|
|
l, err = net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port))
|
|
if err == nil {
|
|
break
|
|
}
|
|
time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
|
|
}
|
|
|
|
return func() {
|
|
_ = l.Close()
|
|
}
|
|
}
|