Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions protocol/fortinet/fgfm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Package fortinet is a very basic (and incomplete) implementation of Fortinet FGFM protocol
package fortinet

import (
"bytes"
"crypto/tls"
"encoding/binary"
"net"

"github.com/vulncheck-oss/go-exploit/output"
"github.com/vulncheck-oss/go-exploit/protocol"

)

// 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
//
// [4 bytes of magic header]
// [4 bytes of total request length]
// [n bytes request body data]

func SendFGFMMessage(conn net.Conn, payload string) bool {
message := make([]byte, 0)
// add magic header
message = append(message, []byte("\x36\xe0\x11\x00")...)
// build the total length field
totalLengthField := make([]byte, 4)
length := len(payload) + 8
binary.BigEndian.PutUint32(totalLengthField, uint32(length))
message = append(message, totalLengthField...)
// add payload
message = append(message, []byte(payload)...)

return protocol.TCPWrite(conn, message)
}

func ReadFGFMMessage(conn net.Conn) ([]byte, bool) {
magic, ok := protocol.TCPReadAmount(conn, 4)
if !ok || !bytes.Equal(magic, []byte("\x36\xe0\x11\x00")) {
output.PrintFrameworkError("Failed to read server response with expected header")
return nil, false

Check failure on line 40 in protocol/fortinet/fgfm.go

View workflow job for this annotation

GitHub Actions / build

return with no blank line before (nlreturn)
}
size, ok := protocol.TCPReadAmount(conn, 4)
if !ok {
output.PrintFrameworkError("Failed to read server response length")
return nil, false

Check failure on line 45 in protocol/fortinet/fgfm.go

View workflow job for this annotation

GitHub Actions / build

return with no blank line before (nlreturn)
}

readSize := int(binary.BigEndian.Uint32(size))
data, ok := protocol.TCPReadAmount(conn, readSize-8)
if !ok {
output.PrintFrameworkError("Failed to read server response data")
return nil, false

Check failure on line 52 in protocol/fortinet/fgfm.go

View workflow job for this annotation

GitHub Actions / build

return with no blank line before (nlreturn)
}

return data, true
}

// Fortimanager requires a connecting Fortigate instance to have a cert.
// SSL is optional here so you have the choice to sign the traffic from the go-exploit framework,
// or so you can send the exploit network traffic through a proxy like socat to sign the traffic for you.
// Benefits to this include being able to generate pcaps of the unencrypted traffic
// between go-exploit and your proxy.
// See CVE-2024-47575 for additional information.
func Connect(host string, port int, ssl bool, certFile string, keyFile string) (net.Conn, bool) {
if ssl {
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
output.PrintFrameworkError("Failed to load x509 Key Pair")
output.PrintfFrameworkDebug("Failed to load x509 Key Pair with error: %s", err)

return nil, false
}
cfg := &tls.Config{Certificates: []tls.Certificate{cert}, InsecureSkipVerify: true}

conn, ok := protocol.TCPConnect(host, port)
if !ok {
return nil, false
}
return tls.Client(conn, cfg), true
}

return protocol.TCPConnect(host, port)

}
Loading