Skip to content

Commit e9c30aa

Browse files
committed
fgfm protocol
1 parent bf9d984 commit e9c30aa

File tree

1 file changed

+84
-0
lines changed

1 file changed

+84
-0
lines changed

protocol/fortinet/fgfm.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Package fortinet is a very basic (and incomplete) implementation of Fortinet FGFM protocol
2+
package fortinet
3+
4+
import (
5+
"bytes"
6+
"crypto/tls"
7+
"encoding/binary"
8+
"net"
9+
10+
"github.com/vulncheck-oss/go-exploit/output"
11+
"github.com/vulncheck-oss/go-exploit/protocol"
12+
13+
)
14+
15+
// Creates a Fortinet FGFM message. The format is closed source, but research by BF, Watchtowr, and Rapid7 have helped uncover the basic message header structure
16+
//
17+
// [4 bytes of magic header]
18+
// [4 bytes of total request length]
19+
// [n bytes request body data]
20+
21+
func SendFGFMMessage(conn net.Conn, payload string) bool {
22+
message := make([]byte, 0)
23+
// add magic header
24+
message = append(message, []byte("\x36\xe0\x11\x00")...)
25+
// build the total length field
26+
totalLengthField := make([]byte, 4)
27+
length := len(payload) + 8
28+
binary.BigEndian.PutUint32(totalLengthField, uint32(length))
29+
message = append(message, totalLengthField...)
30+
// add payload
31+
message = append(message, []byte(payload)...)
32+
33+
return protocol.TCPWrite(conn, message)
34+
}
35+
36+
func ReadFGFMMessage(conn net.Conn) ([]byte, bool) {
37+
magic, ok := protocol.TCPReadAmount(conn, 4)
38+
if !ok || !bytes.Equal(magic, []byte("\x36\xe0\x11\x00")) {
39+
output.PrintFrameworkError("Failed to read server response with expected header")
40+
return nil, false
41+
}
42+
size, ok := protocol.TCPReadAmount(conn, 4)
43+
if !ok {
44+
output.PrintFrameworkError("Failed to read server response length")
45+
return nil, false
46+
}
47+
48+
readSize := int(binary.BigEndian.Uint32(size))
49+
data, ok := protocol.TCPReadAmount(conn, readSize-8)
50+
if !ok {
51+
output.PrintFrameworkError("Failed to read server response data")
52+
return nil, false
53+
}
54+
55+
return data, true
56+
}
57+
58+
// Fortimanager requires a connecting Fortigate instance to have a cert.
59+
// SSL is optional here so you have the choice to sign the traffic from the go-exploit framework,
60+
// or so you can send the exploit network traffic through a proxy like socat to sign the traffic for you.
61+
// Benefits to this include being able to generate pcaps of the unencrypted traffic
62+
// between go-exploit and your proxy.
63+
// See CVE-2024-47575 for additional information.
64+
func Connect(host string, port int, ssl bool, certFile string, keyFile string) (net.Conn, bool) {
65+
if ssl {
66+
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
67+
if err != nil {
68+
output.PrintFrameworkError("Failed to load x509 Key Pair")
69+
output.PrintfFrameworkDebug("Failed to load x509 Key Pair with error: %s", err)
70+
71+
return nil, false
72+
}
73+
cfg := &tls.Config{Certificates: []tls.Certificate{cert}, InsecureSkipVerify: true}
74+
75+
conn, ok := protocol.TCPConnect(host, port)
76+
if !ok {
77+
return nil, false
78+
}
79+
return tls.Client(conn, cfg), true
80+
}
81+
82+
return protocol.TCPConnect(host, port)
83+
84+
}

0 commit comments

Comments
 (0)