FortiGate
FortiGate Next Generation Firewall utilizes purpose-built security processors and threat intelligence security services from FortiGuard labs to deliver top-rated protection and high performance, including encrypted traffic.
fropert_FTNT
Staff
Staff
Description
The FSSO NetAPI polling mode scans a Microsoft Windows domain controller every 9 seconds.
The NetAPI polling use the NetSessionEnum Microsoft API from netapi32.dll to detect the users that have established session on the domain controller.
It must be considered that the bandwidth usage is based on the concurrent logged-in users when the polling action is executed.
The other FSSO polling and DC Agent methods to calculate the bandwidth is not based on concurrent logged-in users but logged-in users per second.


Detection condition:

The users that opens a shared resource on the server.


Sniffer trace during a NETApi polling request:

The following table is an excerpt from Microsoft Network Monitor sniffer trace which contains the packets sequence when the FSSO collector agent binds to the domain controller and collect the usernames:

1	SMB2	SMB2:C   CREATE (0x5), Sh(RWD), File=srvsvc@#1  
2	SMB2	SMB2:R   CREATE (0x5), FID=0xFFFFFFFF003000C9(srvsvc@#1) 
3	MSRPC	MSRPC:c/o Bind: srvs(SRVS) UUID{4B324FC8-1670-01D3-1278-5A47BF6EE188}  Call=0x2  Assoc Grp=0x0  Xmit=0x10B8  Recv=0x10B8 
4	SMB2	SMB2:R   WRITE (0x9), File=srvsvc@#1 
5	SMB2	SMB2:C   READ (0x8), FID=0xFFFFFFFF003000C9  (srvsvc@#1) , 0x400 bytes from offset 0 (0x0) 
6	MSRPC	MSRPC:c/o Bind Ack:  Call=0x2  Assoc Grp=0x2718  Xmit=0x10B8  Recv=0x10B8 
7	SRVS	SRVS:NetrSessionEnum Request, ServerName=WMI1.fsso.local ClientName=NULL UserName=NULL PreferedMaximumLength=4294967295 ResumeHandle=0 (0x00000000)
8	SRVS	SRVS:NetrSessionEnum Response, TotalEntries=2 ResumeHandle=83 (0x00000053) Status=ERROR_SUCCESS
9	SMB2	SMB2:C   CLOSE (0x6), FID=0xFFFFFFFF003000C9(srvsvc@#1)  
10	SMB2	SMB2:R   CLOSE (0x6), File=srvsvc@#1  

The step number 7 contains the domain controller DNS name.
The step number 8 contains the number of users that has a session established on the domain controller.

Network bandwidth usage calculation:

To get a bandwidth usage estimation we should include the size of the 10 packets considering that the length of packets in steps 7 and 8 will change based on the ServerName field length, the number of entries and the length of the usernames included in step 8 during a 9 seconds window.
Steps 1 to 6, 9 and 10 consumes 1553 Bytes.
Step 7 consumes 276 Bytes + the length of the ServerName string value.
Step 8 consumes 213 Bytes + (16 + 56 + (12 + username length * 2 + 2)) * number of users.
Finally, the addition of these steps must be divided by 9 to get an estimation per second.
1000 concurrent logged-in users on 1 domain controller called "SRV1.fsso.local" with an average of 12 characters usernames will use 12450 Bytes per second (1553 + 290 + 110213) / 9.


Script execution:

go run netapi_bwcalc.go 1000 12 WMI1.fsso.local

12450

Script arguments:

1000 = Concurrent logged-in users
12 = Average username length (a username is like 'administrator')
WMI1.fsso.local = Domain controller DNS name

Script result:

The GO script will returns the bandwidth usage in Bytes per second.


Scope
FSSO NetAPI polling mode
Solution
/*
Args:
Concurrent logged-in users: 1000
Average username length : 12
Domain controller DNS name : WMI1.fsso.local
Notes:
- Value returned is in Bytes per second.
Author: Francois Ropert - Fortinet
*/

package main
import (
    "fmt"
    "os"
    "strconv"
)

var smbheaderlen int = 1553 // smb rpc srvsvc
var netsessionenumreqlen int = 276 // netsessionenum request
var netsessionenumrsplen int = 213 // netsessionenum response

func bwcalc(loggedinusers int, usernamelength int, domaincontroller string) int {
  var usage int
  usage = smbheaderlen + (netsessionenumreqlen + len(domaincontroller)) + netsessionenumrsplen + (16 + 56 + (12 + usernamelength * 2 + 2)) * loggedinusers
  return usage / 9
}

func main() {
  logons, err := strconv.Atoi(os.Args[1])
  if err != nil {
      os.Exit(1)
  }
  userlen, err := strconv.Atoi(os.Args[2])
  if err != nil {
      os.Exit(1)
  }
  dcname := os.Args[3]
  fmt.Println(bwcalc(logons, userlen, dcname))
}

Contributors