Skip to main content

Avoiding device management being exposed on interfaces to the internet is something that we all want to avoid, but if such an exposure was to occur wouldn’t you want to know?

This query inspects all interfaces and specifically checks whether http, https, fgfm (FortiManager), snmp, and SSH are enabled via the set allowaccess command.  

To run this check you’ll need to configure a custom command.

Custom Command Required.

show system interfaces

The Query:

/**
* @intent Do not present fgfm, SSH, HTTPS on the internet ports
* @description Check all interfaces that face the internet to ensure
* "SSH","HTTPS" and "fgfm" are not present.

Example Interface:

edit "mgmt1"
set vdom "root"
set ip 169.254.255.2 255.255.255.255
set allowaccess ping https ssh snmp
set status down
set type physical
set dedicated-to management
set role lan
set snmp-index 1
next

*/

ifacePattern =
```
config system interface
edit {ifaceName:string}
set ip {strAddress:string} {strSubnet:string}
set allowaccess {accessPermitted:(string*)}
```;

//List of permissions we want to ensure are not present on internet facing interface
permissionsList = ["fmfg","ssh","snmp","http","https"];
strRfcRanges = ["10.0.0.0/8","172.16.0.0/12","192.168.0.0/16","169.254.0.0/16"]; //rfc1918 Ranges

foreach device in network.devices
where device.platform.vendor == Vendor.FORTINET // Onlyh select fortinet devices, as we only have firewalls, this is sufficent.

foreach command in device.outputs.commands

where command.commandText == "show system interface" //Retrieve only the command show system interface
let response = command.response
let parsedOutput = parseConfigBlocks(OS.FORTINET,response)
let matches = blockMatches(parsedOutput,ifacePattern) //Match the data within the ifacePattern Variable
foreach match in matches

let ipAddr = ipAddress(match.data.strAddress) //Convert String of IP Address into an IP Address

let ifacePerms = match.data.accessPermitted // Create variable for permitted access (Probably not needed but looks a bit neater in my view)

//Set rfcAddress if ipAddress in the rfc1918 Ranges
let rfcAddress = (foreach rfcSubnet in strRfcRanges
where ipAddr in ipSubnet(rfcSubnet)
select{ipAddr})

where length(rfcAddress) == 0 // If this is 0 then this is an internet facing address and we should be checking.
let bannedPerms = (foreach bannedPerm in permissionsList //Using the permissions list check each one in turn to see if the interface has that permission present.
where bannedPerm in ifacePerms
select{ifacePerms})

//Display data if violation occurs as management access is presented to the internet.
select {
violation: length(bannedPerms) > 0,
name:device.name,
ifaceName:match.data.ifaceName,
ip:ipAddr,
ifaceParams:ifacePerms
}

While 169.254.0.0/16 is not part of RFC1918, it appears on non-internet facing interfaces so needed to be excluded.

Any suggestions gratefully received, and i hope it is useful.

For long term maintainability, it might be useful to extract out some named helper functions and to demarcate different parts of the code by a topic header. For example, like this:
 

/**
* @title Internet-facing interfaces must not allow management protocols
* @intent Do not present fgfm, SSH, or HTTPS on Internet-facing ports
* @description
* For Fortinet devices, parse interface definitions and flag any Internet-facing
* interface whose `allowaccess` includes disallowed management protocols.
*
* Default banned protocols: fgfm, ssh, https
* (Optionally extend to http, snmp, etc. by editing BANNED_PERMS_DEFAULT.)
*
* "Internet-facing" = interface IP not in RFC1918 or link-local ranges
* (10/8, 172.16/12, 192.168/16, 169.254/16).
*
* Example Interface:
* edit "mgmt1"
* set vdom "root"
* set ip 169.254.255.2 255.255.255.255
* set allowaccess ping https ssh snmp
* ...
*/

//
// ----------------------------- Constants ----------------------------------
//

// Pattern for Fortinet interface blocks we care about.
ifacePattern =
```
config system interface
edit {ifaceName:string}
set ip {strAddress:string} {strSubnet:string}
set allowaccess {accessPermitted:(string*)}
```;

// Default list of disallowed management protocols (lowercase).
BANNED_PERMS_DEFAULT = ["fgfm", "ssh", "https"];

// RFC1918 + link-local ranges used to identify non-Internet interfaces.
RFC_RANGES =
["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "169.254.0.0/16"];

//
// --------------------------- Helper Functions -----------------------------
//

/**
* Parse the output of "show system interface" for a Fortinet device into config blocks.
* Returns a parsed structure consumable by blockMatches(...).
*/
parseFortinetInterfaces(device) =
foreach command in device.outputs.commands
where command.commandText == "show system interface" //Retrieve only the command show system interface
let response = command.response
let parsedOutput = parseConfigBlocks(OS.FORTINET, response)
select parsedOutput;

/** Extract interface matches (name, IP, allowaccess) from parsed blocks using ifacePattern. */
interfaceMatches(blocks) = blockMatches(blocks, ifacePattern);

/** Checks whether aList is empty or not. */
isEmpty(aList) = length(aList) == 0;

/** True iff the IP falls into any of the provided RFC_RANGES. */
isInternetFacing(ipAddr) =
isEmpty(foreach rfcSubnet in RFC_RANGES
where ipAddr in ipSubnet(rfcSubnet)
select 1);

/** True iff ifacePerms contains any banned permission in BANNED_PERMS_DEFAULT. */
hasBannedPermission(ifacePerms) =
!isEmpty(foreach bannedPerm in BANNED_PERMS_DEFAULT
where bannedPerm in ifacePerms
select { ifacePerms });

/** Get information about the given `device`, including whether is `hasBannedPermission`. */
relevantInformation(device) =
foreach parsedOutput in parseFortinetInterfaces(device)
let matches = interfaceMatches(parsedOutput)
foreach match in matches
let ipAddr = ipAddress(match.data.strAddress) // Convert String of IP Address into an IP Address
where isInternetFacing(ipAddr)
let ifacePerms = match.data.accessPermitted
select {
violation: hasBannedPermission(ifacePerms),
name: device.name,
ipAddr,
ifacePerms,
ifaceName: match.data.ifaceName
};

//
// ----------------------------- Main Query ---------------------------------
//

foreach device in network.devices
where device.platform.vendor == Vendor.FORTINET
foreach relevantInfo in relevantInformation(device)
select relevantInfo

Take this with a grain of salt 🧂 😁