Ligolo is alive

This commit is contained in:
Nicolas Chatelain
2020-05-22 10:02:53 +02:00
commit db58298f83
12 changed files with 1293 additions and 0 deletions
+163
View File
@@ -0,0 +1,163 @@
package main
import (
"bytes"
"crypto/sha256"
"crypto/tls"
"encoding/hex"
"flag"
"fmt"
"github.com/armon/go-socks5"
"github.com/hashicorp/yamux"
"github.com/sirupsen/logrus"
"io"
"net"
"strings"
"time"
)
var tlsFingerprint string
var (
ErrInvalidServerCert = fmt.Errorf("invalid TLS server certificate")
ErrInvalidPinnedCert = fmt.Errorf("invalid TLS pinned certificate")
)
func main() {
fmt.Print(`
██╗ ██╗ ██████╗ ██████╗ ██╗ ██████╗
██║ ██║██╔════╝ ██╔═══██╗██║ ██╔═══██╗
██║ ██║██║ ███╗██║ ██║██║ ██║ ██║
██║ ██║██║ ██║██║ ██║██║ ██║ ██║
███████╗██║╚██████╔╝╚██████╔╝███████╗╚██████╔╝
╚══════╝╚═╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚═════╝
Local Input - Go - Local Output
`)
bypassVerify := flag.Bool("skipverify", false, "Skip TLS certificate pinning verification")
targetServer := flag.String("targetserver", "", "The destination server (a RDP client, SSH server, etc.) - when not specified, Ligolo starts a socks5 proxy server")
relayServer := flag.String("relayserver", "127.0.0.1:5555", "The relay server (the connect-back address)")
autoRestart := flag.Bool("autorestart", false, "Attempt to reconnect in case of an exception")
flag.Parse()
if tlsFingerprint == "" && *bypassVerify == false {
logrus.Fatal("TLS Fingerprint is missing ! Use -skipverify option to bypass TLS verification")
}
for {
err := StartLigolo(*relayServer, *targetServer, *bypassVerify)
if err != nil {
if *autoRestart {
logrus.Error(err)
} else {
logrus.Fatal(err)
}
}
logrus.Warning("Restarting Ligolo...")
time.Sleep(10 * time.Second)
}
}
func StartLigolo(relayServer string, targetServer string, skipVerify bool) error {
var socks *socks5.Server
logrus.Infoln("Connecting to relay server...")
config := &tls.Config{InsecureSkipVerify: true}
conn, err := tls.Dial("tcp", relayServer, config)
if err != nil {
return err
}
if !skipVerify {
err := verifyTlsCertificate(conn.ConnectionState())
if err != nil {
logrus.WithFields(logrus.Fields{"remoteaddr": conn.RemoteAddr().String()}).Error(err)
return err
}
}
if targetServer == "" {
socks, err = startSocksProxy()
if err != nil {
logrus.Error("Could not start SOCKS5 proxy !")
return err
}
}
session, err := yamux.Client(conn, nil)
if err != nil {
return err
}
logrus.Infoln("Waiting for connections....")
for {
stream, err := session.Accept()
if err != nil {
return err
}
logrus.WithFields(logrus.Fields{"active_sessions": session.NumStreams()}).Println("Accepted new connection !")
// When no targetServer are specified, starts a socks5 proxy
if targetServer == "" {
go socks.ServeConn(stream)
} else {
proxyConn, err := net.Dial("tcp", targetServer)
if err != nil {
logrus.Errorf("Error creating Proxy TCP connection ! Error : %s\n", err)
return err
}
go handleRelay(stream, proxyConn)
}
}
}
func startSocksProxy() (*socks5.Server, error) {
conf := &socks5.Config{}
socks, err := socks5.New(conf)
if err != nil {
logrus.Error("Could not start SOCKS5 proxy !")
return nil, err
}
return socks, nil
}
func verifyTlsCertificate(connState tls.ConnectionState) error {
valid := false
pinnedCert := strings.Replace(tlsFingerprint, ":", "", -1)
pinnedCertBytes, err := hex.DecodeString(pinnedCert)
if err != nil {
return ErrInvalidPinnedCert
}
for _, peerCert := range connState.PeerCertificates {
hash := sha256.Sum256(peerCert.Raw)
if bytes.Compare(hash[:], pinnedCertBytes) == 0 {
valid = true
}
}
if !valid {
return ErrInvalidServerCert
}
return nil
}
func handleRelay(src net.Conn, dst net.Conn) {
stop := make(chan bool, 2)
go relay(src, dst, stop)
go relay(dst, src, stop)
select {
case <-stop:
return
}
}
func relay(src net.Conn, dst net.Conn, stop chan bool) {
io.Copy(dst, src)
dst.Close()
src.Close()
stop <- true
return
}
+168
View File
@@ -0,0 +1,168 @@
package main
import (
"crypto/tls"
"flag"
"fmt"
"github.com/hashicorp/yamux"
"github.com/sirupsen/logrus"
"io"
"net"
)
func main() {
fmt.Print(`
██╗ ██╗ ██████╗ ██████╗ ██╗ ██████╗
██║ ██║██╔════╝ ██╔═══██╗██║ ██╔═══██╗
██║ ██║██║ ███╗██║ ██║██║ ██║ ██║
██║ ██║██║ ██║██║ ██║██║ ██║ ██║
███████╗██║╚██████╔╝╚██████╔╝███████╗╚██████╔╝
╚══════╝╚═╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚═════╝
Local Input - Go - Local Output
`)
localServer := flag.String("localserver", "127.0.0.1:1080", "The local server address (your proxychains parameter)")
relayServer := flag.String("relayserver", "0.0.0.0:5555", "The relay server listening address (the connect-back address)")
certFile := flag.String("certfile", "certs/cert.pem", "The TLS server certificate")
keyFile := flag.String("keyfile", "certs/key.pem", "The TLS server key")
flag.Parse()
relay := NewLigoloRelay(*localServer, *relayServer, *certFile, *keyFile)
relay.Start()
}
// LigoloRelay structure contains configuration, the current session and the ConnectionPool
type LigoloRelay struct {
LocalServer string
RelayServer string
CertFile string
KeyFile string
ConnectionPool chan *yamux.Session
Session *yamux.Session
}
// NewLigoloRelay creates a new LigoloRelay struct
func NewLigoloRelay(localServer string, relayServer string, certFile string, keyFile string) *LigoloRelay {
return &LigoloRelay{LocalServer: localServer, RelayServer: relayServer, CertFile: certFile, KeyFile: keyFile, ConnectionPool: make(chan *yamux.Session, 100)}
}
// Start listening for local and relay connections
func (ligolo LigoloRelay) Start() {
logrus.WithFields(logrus.Fields{"localserver": ligolo.LocalServer, "relayserver": ligolo.RelayServer}).Println("Ligolo server started.")
go ligolo.startRelayHandler()
ligolo.startLocalHandler()
}
// Listen for Ligolo connections
func (ligolo LigoloRelay) startRelayHandler() {
cer, err := tls.LoadX509KeyPair(ligolo.CertFile, ligolo.KeyFile)
if err != nil {
logrus.Error("Could not load TLS certificate.")
return
}
config := &tls.Config{Certificates: []tls.Certificate{cer}}
listener, err := tls.Listen("tcp4", ligolo.RelayServer, config)
if err != nil {
logrus.Errorf("Could not bind to port : %v\n", err)
return
}
defer listener.Close()
for {
c, err := listener.Accept()
if err != nil {
logrus.Errorf("Could not accept connection : %v\n", err)
return
}
session, err := handleRelayConnection(c)
if err != nil {
logrus.Errorf("Could not start session : %v\n", err)
continue
}
ligolo.ConnectionPool <- session
}
}
// Listen for local connections
func (ligolo LigoloRelay) startLocalHandler() {
listener, err := net.Listen("tcp4", ligolo.LocalServer)
if err != nil {
fmt.Println(err)
return
}
defer listener.Close()
ligolo.Session = <- ligolo.ConnectionPool
go func(){
for {
<- ligolo.Session.CloseChan()
logrus.WithFields(logrus.Fields{"remoteaddr": ligolo.Session.RemoteAddr()}).Println("Received session shutdown.")
ligolo.Session = <- ligolo.ConnectionPool
logrus.WithFields(logrus.Fields{"remoteaddr": ligolo.Session.RemoteAddr()}).Println("New session acquired.")
}
}()
logrus.Println("Session acquired. Starting relay.")
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println(err)
return
}
go ligolo.handleLocalConnection(conn)
}
}
// Handle new local connections
func (ligolo LigoloRelay) handleLocalConnection(conn net.Conn) {
if ligolo.Session.IsClosed(){
logrus.Warning("Closing connection because no session available !")
conn.Close()
return
}
logrus.Println("New proxy connection. Establishing new session.")
stream, err := ligolo.Session.Open()
if err != nil {
logrus.Errorf("Could not open session : %s\n", err)
return
}
logrus.Println("Yamux session established.")
go relay(conn, stream)
go relay(stream, conn)
select {
case <-ligolo.Session.CloseChan():
logrus.WithFields(logrus.Fields{"remoteaddr": ligolo.Session.RemoteAddr().String()}).Println("Connection closed.")
return
}
}
// Handle new ligolo connections
func handleRelayConnection(conn net.Conn) (*yamux.Session, error) {
logrus.WithFields(logrus.Fields{"remoteaddr": conn.RemoteAddr().String()}).Info("New relay connection.\n")
session, err := yamux.Server(conn, nil)
if err != nil {
return nil, err
}
ping, err := session.Ping()
if err != nil {
return nil, err
}
logrus.Printf("Session ping : %v\n", ping)
return session, nil
}
func relay(src net.Conn, dst net.Conn) {
io.Copy(dst, src)
dst.Close()
src.Close()
return
}