Hunt FortiBleed: the firewall admin you didn't create


The firewall admin signs in on a Monday and the password is wrong. It worked on Friday. The admin list now holds an account called something like forticloud-sync that nobody remembers creating. Someone knew a password that still worked.
On 6 October 2026, the FBI and the US Secret Service published joint advisory JCSA-20261006-01 on FortiBleed, "an active, global credential-compromise campaign" against internet-facing FortiGate firewalls and SSL VPN gateways. SOCRadar, cited by the FBI, verified more than 86,644 compromised devices across 194 countries. The new part is the ending: affected organizations "may find themselves locked out of their systems," which requires "remediation steps beyond standard patching and password resets," and the chain has served as "an initial entry point for ransomware affiliates."
Why this one is different
FortiBleed is not a CVE. In June, Fortinet said "this is not a new Fortinet vulnerability," tying the activity to credentials reused from FG-IR-26-060 and FG-IR-25-647 plus brute force against devices with weak passwords and no MFA. CISA warned on 18 June. This week the FBI says scanning continues "using previously obtained compromised credentials," now with reports of lockouts.
A firmware upgrade does not remove an admin someone else created, and a reset does not help if your account is already gone.
The attacker logged in with a real password, so the alert you need is the account that should not exist.
Ask yourself two questions. Could you name who created every admin on every FortiGate today? And if a VPN address failed logins against dozens of domain accounts in an hour, would anyone notice?
How a leaked password becomes a locked-out firewall
The FBI traced the full chain from a leaked password to a locked-out firewall. Here is how it works, in three stages.
From leak dump to a valid login
The FBI says the workflow became visible "only after the threat actors unintentionally exposed their own backend server." The operators scanned for exposed FortiGate SSL VPN portals, then ran credential stuffing and password spraying "based on prior Fortinet leak dumps and infostealer logs." Hashes dumped from compromised devices went to a rented GPU cluster for offline cracking. Working credentials were validated and targets ranked "based on revenue and network structure."
From admin to owner
Once in, the operators "created new administrative accounts on FortiGate devices to maintain persistent access." In certain cases they also deleted existing accounts "to block organizations from accessing affected devices." The FBI adds that they "may have exploited SSH if the port was open on the firewall."
Our reading: a firewall admin controls VPN policy, routing, and logging, so the first thing an intruder can take away is your visibility into what they are doing.
From the firewall to the domain, then to a buyer
Inside, the operators ran Active Directory enumeration and password spraying to find privileged accounts. The FBI says the operation ended in "packaging and selling access," and that initial access brokers using the chain have supplied ransomware affiliates, "currently including INC/Lynx ransomware and Payload ransomware."
What the FBI published and what it did not
The FBI published the chain above, ATT&CK mappings (MITRE's catalog of attacker techniques) including Account Access Removal (T1531) and Local Account Creation (T1136.001), and network indicators: a command-and-control (C2) server at 45[.]154[.]12[.]132, a beacon relay at 45[.]155.]250[.]158, two proxy nodes, and three supporting addresses, one used for Hashtopolis (a distributed password-cracking tool). The actors use ports including 4332 and 4432, "but usually beaconing to" HTTPS. Table 1 lists 13 addresses seen brute-forcing or authenticating between 18 June and 23 July 2026, and Table 2 lists 19 account names found on victim devices. Fortinet's own examples add forticloud, fortiuser, fortinet-support, and fortinet-tech-support.
It did not publish victim names, a count of lockouts, sectors, the broker's name, or file hashes. It warns that Table 1 addresses "may later be reassigned to benign services" and says to vet indicators "prior to acting, such as blocking."
What to change this week
If the hunts below find a hit, follow the FBI's order: isolate, scope, report to the FBI or USSS, then evict. Fortinet says to treat a device with unapproved configuration changes as compromised, along with any AD or LDAP account it uses. Whether or not you find a hit, do these in order:
Terminate all active admin and VPN sessions. A reset does nothing to an open session.
Reset all VPN and admin passwords and require phishing-resistant MFA on every admin and remote access account.
Compare each configuration to a known good copy and remove any account you did not create.
Review every REST API key, remove unknown ones, and refresh the rest, as the FBI avises.
Then take management off the internet: trusted hosts are good, a local-in policy is better, no internet administration is best. Our reading: treat old config backups as
credential material, since dumped hashes fed this campaign.
Four hunts to run (test first)
The FBI says to review firewall, VPN, authentication, and domain controller logs and verify every Fortinet account. Each query is our translation into field names, so test each one first. The hunts skip admin from Table 2 because it is the FortiGate default; count it only if it was created recently. Set the window back to at least 18 June. The table under the hunts, "Interpret before you escalate", says what is benign and what is not.
Hunt in Splunk: admin changes, named accounts, and FBI addresses (test first)
index=YOUR_FORTIGATE_INDEX type=event (subtype=system OR subtype=vpn) earliest=-120d
| eval src=coalesce(srcip, remip), acct=lower(coalesce(cfgobj, user)), act=coalesce(vendor_action, action)
| eval signals=mvappend(
if(cfgpath="system.admin" AND match(act, "(?i)^(add|delete|edit)$"), "admin_object_change", null()),
if(match(logdesc, "(?i)admin login successful") AND match(ui, "(?i)^ssh"), "admin_ssh_login", null()),
if(in(acct, "adminin","fortiadmin","forticloud-sync","fgtsecure","pakedge","forticloud-tech","districtadmin","system_config","gttadmin","roadmin","itadmin","technical_support","adminsslvpn","it_manager","my_admin","support_fortinet","fgtsec","forti_support2","forticloud","fortiuser","fortinet-support","fortinet-tech-support"), "named_account", null()),
if(in(src, "104.28.155.27","185.136.15.43","185.136.15.66","185.199.199.56","193.8.186.33","45.227.254.210","77.91.118.10","80.75.212.113","87.251.64.13","87.251.64.16","87.251.64.17","87.251.64.44","66.175.220.111","45.154.12.132","154.202.59.169","103.27.186.156","45.155.250.158","193.8.187.2","193.8.187.42","85.11.187.8"), "fbi_ip", null()))
| where isnotnull(signals)
| stats min(_time) as first_seen, max(_time) as last_seen, values(signals) as signals, values(act) as actions, values(src) as sources, values(ui) as ui by devname, acct
| convert ctime(first_seen) ctime(last_seen)
| sort 0 first_seenField mapping: FortiGate syslog via the Fortinet add-on. Admin changes are log ID 44547 and carry cfgpath, cfgobj, and action; SSL VPN events put the remote address in remip. The add-on keeps the raw action in vendor_action, so the query reads that first. Rename if your sourcetype differs. Test first.
False positives: planned admin changes; MSP or installer accounts named like itadmin or IT_Manager. Edit covers password changes and routine edits alike; a password change shows password[*] in cfgattr.
Empty result: confirm config change events are forwarded, not just traffic. Our reading: an unexplained named_account plus Add is worth a call on its own.
Hunt in Kibana: rogue admins, then the failure burst that turned into a session (test first)
Search 1: rogue admins, named accounts, and FBI addresses
(fortinet.firewall.cfgpath : "system.admin" and fortinet.firewall.action : ("Add" or "Delete" or "Edit"))
or user.name : ("adminin" or "fortiAdmin" or "forticloud-sync" or "fgtsecure" or "pakedge" or "forticloud-tech" or "districtadmin" or "system_config" or "gttadmin" or "roadmin" or "itadmin" or "Technical_support" or "adminsslvpn" or "IT_Manager" or "my_admin" or "support_fortinet" or "fgtsec" or "forti_support2" or "forticloud" or "fortiuser" or "fortinet-support" or "fortinet-tech-support")
or source.ip : (104.28.155.27 or 185.136.15.43 or 185.136.15.66 or 185.199.199.56 or 193.8.186.33 or 45.227.254.210 or 77.91.118.10 or 80.75.212.113 or 87.251.64.13 or 87.251.64.16 or 87.251.64.17 or 87.251.64.44 or 66.175.220.111 or 154.202.59.169 or 103.27.186.156 or 193.8.187.2 or 193.8.187.42 or 85.11.187.8)
or destination.ip : (45.154.12.132 or 45.155.250.158)Search 2: a failure burst that becomes a tunnel
sequence by source.ip with maxspan=1h
[any where observer.vendor == "Fortinet" and fortinet.firewall.action == "ssl-login-fail"] with runs=10
[any where observer.vendor == "Fortinet" and fortinet.firewall.action == "tunnel-up"]Field mapping: KQL in Discover over the Elastic Fortinet integration, then EQL in Timelines. The integration keeps FortiGate event actions in fortinet.firewall.action and moves the SSL VPN remip into source.ip. Group by observer.name and user.name. Test first.
False positives: a user who forgot a password and then got it right; a scanner you own.
Empty result: confirm the integration ships event logs and keeps fortinet.firewall.cfgpath.
Hunt in Falcon CQL: admin changes, then spraying from the VPN pool (test first)
Search 1: admin changes, named accounts, and FBI addresses on the FortiGate
#Vendor="fortinet"
| coalesce([source.ip, Vendor.remip], as=client_ip)
| case {
@rawstring=/cfgpath="?system\.admin/i @rawstring=/action="?(Add|Delete|Edit)\b/i | signal := "admin_object_change" ;
@rawstring=/(user|cfgobj)="?(adminin|fortiAdmin|forticloud-sync|fgtsecure|pakedge|forticloud-tech|districtadmin|system_config|gttadmin|roadmin|itadmin|Technical_support|adminsslvpn|IT_Manager|my_admin|support_fortinet|fgtsec|forti_support2|forticloud|fortiuser|fortinet-support|fortinet-tech-support)"?(\s|$)/i | signal := "named_account" ;
in(field="client_ip", values=["104.28.155.27","185.136.15.43","185.136.15.66","185.199.199.56","193.8.186.33","45.227.254.210","77.91.118.10","80.75.212.113","87.251.64.13","87.251.64.16","87.251.64.17","87.251.64.44","66.175.220.111","154.202.59.169","103.27.186.156","193.8.187.2","193.8.187.42","85.11.187.8"]) | signal := "fbi_ip" ;
in(field="destination.ip", values=["45.154.12.132","45.155.250.158"]) | signal := "c2_or_relay" ;
* | signal := "" ;
}
| signal != ""
| regex("devname=\"?(?<devname>[^\"\s]+)", field=@rawstring, strict=false)
| groupBy([devname, signal], function=[min(@timestamp, as=first_seen), count()])
| sort(first_seen, order=asc)Search 2: domain logon failures fanning out from the VPN pool
#event_simpleName=UserLogonFailed2
| cidr(RemoteAddressIP4, subnet="YOUR_SSL_VPN_POOL/24")
| bucket(span=1h, field=[RemoteAddressIP4], function=[count(UserName, distinct=true, as=accounts), count(as=failures)])
| accounts >= 15Field mapping: Falcon Next-Gen SIEM with the FortiGate parser, then sensor data from domain controllers. The parser files the SSL VPN remip under destination.ip, so the query reads Vendor.remip too. Set your SSL VPN pool and tune the threshold. Test first.
False positives: a misconfigured service retrying from a VPN user's laptop; a sanctioned scanner.
Empty result: confirm the parser fills source.ip or Vendor.remip and that Kerberos failures reach the platform.
Hunt in KQL: Fortinet CEF plus domain controller fan-out (test first)
let Window = ago(120d);
let FbiIps = dynamic(["104.28.155.27","185.136.15.43","185.136.15.66","185.199.199.56","193.8.186.33","45.227.254.210","77.91.118.10","80.75.212.113","87.251.64.13","87.251.64.16","87.251.64.17","87.251.64.44","66.175.220.111","154.202.59.169","103.27.186.156","193.8.187.2","193.8.187.42","85.11.187.8"]);
let C2 = dynamic(["45.154.12.132","45.155.250.158"]);
let Names = dynamic(["adminin","fortiadmin","forticloud-sync","fgtsecure","pakedge","forticloud-tech","districtadmin","system_config","gttadmin","roadmin","itadmin","technical_support","adminsslvpn","it_manager","my_admin","support_fortinet","fgtsec","forti_support2","forticloud","fortiuser","fortinet-support","fortinet-tech-support"]);
let VpnPool = "YOUR_SSL_VPN_POOL/24";
let Fgt = CommonSecurityLog
| where TimeGenerated >= Window
| where DeviceVendor == "Fortinet"
| extend CfgObj = tolower(extract(@"FTNTFGTcfgobj=([^;\s]+)", 1, AdditionalExtensions))
| extend Acct = tolower(coalesce(CfgObj, SourceUserName, DestinationUserName))
| extend Src = coalesce(SourceIP, extract(@"FTNTFGTremip=([^;\s]+)", 1, AdditionalExtensions))
| extend Device = coalesce(DeviceName, DeviceExternalID, Computer)
| extend Signal = case(
AdditionalExtensions has "system.admin" and DeviceAction in~ ("Add", "Delete", "Edit"), "admin_object_change",
Acct in (Names), "named_account",
Src in (FbiIps), "fbi_ip",
DestinationIP in (C2) or DestinationPort in (4332, 4432), "c2_or_beacon_port",
"")
| where Signal != ""
| summarize FirstSeen = min(TimeGenerated), Signals = make_set(Signal), Accounts = make_set(Acct, 20) by Device;
let Spray = SecurityEvent
| where TimeGenerated >= Window
| where EventID in (4625, 4771)
| extend Src = trim_start(@"::ffff:", IpAddress)
| where ipv4_is_in_range(Src, VpnPool)
| summarize Accounts = make_set(TargetUserName, 20), Distinct = dcount(TargetUserName), FirstSeen = min(TimeGenerated) by Src, bin(TimeGenerated, 1h)
| where Distinct >= 15
| project FirstSeen, Device = Src, Signals = dynamic(["vpn_pool_ad_fanout"]), Accounts;
union Fgt, Spray
| order by FirstSeen ascField mapping: Sentinel with Fortinet CEF in CommonSecurityLog and domain controller Security events. FortiOS sends user as duser, action as act, and unmapped fields such as cfgobj and remip with an FTNTFGT prefix, so check one admin change first. Test first.
False positives: legitimate services on 4332 or 4432 match the port clause alone; only the two FBI addresses are specific.
Empty result: confirm the firewall's own outbound traffic is logged, and that 4771 is audited on domain controllers.
Interpret before you escalate
Finding | Benign read | Escalate when |
Admin added on system.admin | Planned change | No change record, or the creator is itself new |
Original admin deleted or re-passworded | Offboarding or rotation | A new admin appeared the same day |
Table 2 or Fortinet example account | Documented MSP account | Nobody knows who created it |
Login from an FBI listed address | Address reassigned since July | Successful login between 18 June and 23 July 2026 |
Firewall traffic to the C2 or relay | None we know of | Always; treat the device as compromised |
SSL VPN failure burst, then a tunnel | Forgotten password | Domain failures fan out from that tunnel address |
VPN pool address failing on many accounts | Misconfigured service | Many distinct accounts in an hour |
Empty results | Nothing matched | Config, VPN, or DC logs are missing or retention stops short of June, so fix coverage first |
Sources
FBI and USSS joint Cybersecurity Advisory JCSA-20261006-01, FortiBleed Operations Continue Targeting Exposed Systems Leading to Reports of Lockouts, 6 October 2026: https://www.ic3.gov/CSA/2026/261006.pdf
Fortinet PSIRT, Analysis of Reported Credential Compromise of FortiGate Devices, 19 June 2026: https://www.fortinet.com/blog/psirt-blogs/analysis-of-reported-credential-compromise-of-fortigate-devices
Fortinet Community, Technical Tip: Enforcing PBKDF2 as hash function for administrator accounts in FortiOS v7.2.11 and later: https://community.fortinet.com/fortigate-3/technical-tip-enforcing-pbkdf2-as-hash-function-for-administrator-accounts-in-fortios-v7-2-11-and-later-220652
Fortinet FortiGate administration guide, REST API administrator: https://docs.fortinet.com/document/fortigate/8.0.0/administration-guide/399023/rest-api-administrator
CISA, CISA Urges Hardening Fortinet Devices After Reports of Credential Exposure, 18 June 2026: https://www.cisa.gov/news-events/alerts/2026/06/18/cisa-urges-hardening-fortinet-devices-after-reports-credential-exposure
What This Means for Your Team
Stop treating this as a patch window. Kill sessions, reset, and turn on MFA, then pull the admin list from every FortiGate and make someone sign their name next to each account. Run the four hunts back to 18 June. An admin nobody created is the seam to chase, and a VPN address spraying your domain controllers means the firewall was only the front door.
Inception Protection
Inception Protection is our managed detection and response service, and it works with the Microsoft licenses you already have. A chain like this starts on the firewall and ends in Active Directory, so we read firewall logs next to Defender, Entra sign-in, and domain controller signals as one story and move on accounts fast. You keep the decisions.
Inception Foresight
If you want to see how well your Microsoft environment would surface activity like this, the free Inception Foresight M365 Assessment is a good place to start: https://www.inceptionsecurity.com/m365assessment. You can also follow Inception Security on LinkedIn and @inceptionsec on X.



