Stormshield Management Center and TOTP (FortiToken)
Table of Contents
Introduction
Among the manufacturer I work the most with, we can find Stormshield, a European network security manufacturer which I’ll certainly talk a lot about in my posts.
For a recent project with a customer, we had to deploy a Stormshield Management Center, aka SMC, which is used to manage and deploy configuration on the firewalls. The product may be a little bit new on the market (only a few years) and of course, may lack some capabilities compared to other big players in the market.
In my case, something was missing, or at least I thought, the support of 2FA/MFA capabilities. Even if this has been announced for the firewalls, Stormshield Network Security, aka SNS, on their latest version, the SMC does not, yet, support TOTP.
The context
Quick context, we have an SMC to which multiple administrators should connect to administer the policies and firewalls. The previous centralized console allowed to connect using only a username (dedicated to management) and a TOTP generated through the FortiToken Mobile application (it could also be a hardware FortiToken). No “classic” passwords were involved.
On the other side, FortiTokens are managed by a FortiAuthenticator and users are pulled from an Active Directory with sync rules.
The central console and the FortiAuthenticator communicate using RADIUS protocol which allows support to 2FA/MFA, usually using Access-Challenge requests. Since we only use TOTP as the password this won’t be necessary and the central console doesn’t have to support it. We could also use the “concatenation” method by appending the TOTP to the password. This method, also supported by the FortiAuthenticator, could be used.
When we naively try to replicate the configuration on the SMC we get an error “The authentication failed”.
Looking at the log on the FortiAuthenticator, we see two entries:
- The first log shows a successful authentication with the FortiToken;
- The second log shows an authentication failure because of the reuse of the previous token;
At this point, I remembered a discussion I had with the customer who told me they can use the token only one times (per minute for the default FortiToken configuration) even with connections to multiple administrative interfaces. This is smart since this makes the TOTP truly one-time and help prevent replay attack on a compromised network. This is not perfect but it adds another layer of protection.
So our issue might be there, but then, why is the SMC sending multiple requests to the FortiAuthenticator?
Analysis
The quickest way to start understanding what is happening is first to sniff some traffic. The SMC allows us to use tcpdump
which is easier.
[root@smc] - {~} > tcpdump -ni eth0 -vvvvvvv host 10.200.1.6
tcpdump: listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
19:45:38.189282 IP (tos 0x0, ttl 64, id 63341, offset 0, flags [DF], proto UDP (17), length 79)
10.200.1.96.56003 > 10.200.1.6.1812: [bad udp cksum 0x1842 -> 0x96b6!] RADIUS, length: 51
Access-Request (1), id: 0xdc, Authenticator: 59eb6e89caebebc62cceb9db5ce51594
User-Name Attribute (1), length: 13, Value: adm-amoreau
0x0000: 6164 6d2d 616d 6f72 6561 75
User-Password Attribute (2), length: 18, Value:
0x0000: 2830 955a 089f 0e72 631a 2806 027e 55cd
19:45:38.195764 IP (tos 0x0, ttl 64, id 47246, offset 0, flags [none], proto UDP (17), length 48)
10.200.1.6.1812 > 10.200.1.96.56003: [udp sum ok] RADIUS, length: 20
Access-Accept (2), id: 0xdc, Authenticator: 9a24ecd84a63e757a2bd863923dfef0d
19:45:38.235247 IP (tos 0x0, ttl 64, id 63383, offset 0, flags [DF], proto UDP (17), length 108)
10.200.1.96.59994 > 10.200.1.6.1812: [bad udp cksum 0x185f -> 0xeea1!] RADIUS, length: 80
Access-Request (1), id: 0xa0, Authenticator: e4fda89bf84b8decbf86108d54092083
User-Name Attribute (1), length: 13, Value: adm-amoreau
0x0000: 6164 6d2d 616d 6f72 6561 75
User-Password Attribute (2), length: 18, Value:
0x0000: a7b4 2a42 661a aad9 a501 041a aa10 c013
NAS-IP-Address Attribute (4), length: 6, Value: 127.0.0.1
0x0000: 7f00 0001
NAS-Identifier Attribute (32), length: 5, Value: smc
0x0000: 736d 63
NAS-Port Attribute (5), length: 6, Value: 12900
0x0000: 0000 3264
NAS-Port-Type Attribute (61), length: 6, Value: Virtual
0x0000: 0000 0005
Service-Type Attribute (6), length: 6, Value: Authenticate Only
0x0000: 0000 0008
19:45:39.244513 IP (tos 0x0, ttl 64, id 47466, offset 0, flags [none], proto UDP (17), length 48)
10.200.1.6.1812 > 10.200.1.96.59994: [udp sum ok] RADIUS, length: 20
Access-Reject (3), id: 0xa0, Authenticator: ad3876b01a6d3f36530ad01da7b6b577
10.200.1.6 is my FortiAuthenticator and 10.200.1.96 is my SMC
The network dump clearly shows we have two distinct RADIUS requests :
- The first request only shows two attributes: the username and the user password. The FortiAuthenticator replies to it with an
Access-Accept
RADIUS response which matches what we saw in the logs; - The second request now shows more attributes of the NAS itself. Since the password is, supposedly, the same, the FortiAuthenticator rejects the authentication, the
Access-Reject
response, as part of the anti-replay feature. This, again, match what is in the logs;
After a bit of discussion with Stormshield’s support, it appears these two requests are currently “incompressible” and they asked me to create an exception for the SMC. It appears impossible to disable the anti-replay feature on the FortiAuthenticator currently so this was not an option.
Bypass
After a little bit of digging and poking around, I eventually found a way to get it working. Our main issue is that the TOTP is used twice on the FortiAuthenticator side. But is possible to filter the request so only the second one is evaluated by the FortiAuthenticator? We cannot filter the RADIUS request dynamically but we can use RADIUS attribute filtering since both requests don’t have the same attributes.
The first one only contains the username and user password while the second one contains NAS attributes. In my case, I’m using the NAS-Identifier
to filter the RADIUS requests.
So now we have two RADIUS policies, one matching any request from the SMC and the other matching only the second one. The trick is to configure the policy for the second request to use OTP-Only (or all configured password and OTP factors if you plan to use the password & OTP concatenation method) while the RADIUS policy for the first request uses “Password-only” authentication factors.
We then need to place the policy for the second request above the other policy, used for the first request. This way :
- The first request will match the policy dedicated to it and the OTP will be “matched” against the password used. The authentication will fail but it seems not to be an issue for the SMC to receive an
Access-Reject
; - Then, the SMC will request the FortiAuthenticator a second time but this time, the request will match the other policy and the OTP will be compared to the OTP expected value. This time, if everything goes well, the authentication should succeed;
Additional notes
There is a little caveat with this solution, it creates two entries in the logs. On failed authentication followed by a successful authentication.
We could also have used push notification and tweaked the RADIUS timeout configuration but it was not an option in my case with a mostly offline environment.