My customer allows a small number of senior engineers to operate their SCADA platform remotely using Citrix Gateway to access Citrix Virtual Apps and Desktops published resources. The customer’s security team asked if a “duress code” could be implemented that would silently trigger alarms and change certain properties of the environment.
The concept, they explained, was that a remote user placed under duress could reverse their RADIUS PIN during login, and Citrix ADC could somehow detect this situation and trigger changes. Ideally, those changes would include Citrix ADC writing entries to syslog for their security team to act on, directing the user to a server with session recording enabled, disabling access to critical applications, or switching the user to an entirely separate decoy environment where actions that might otherwise lead to loss or damage wouldn’t be executed.
With nFactor guidance from Dileep in our engineering team, the “duress code” functionality is now working perfectly, and as I imagine such functionality could be widely applicable, I’ve decided to share it here.
The “duress code” logic initially presents the user with a standard MFA login screen requesting the user’s credentials as usual. From the user’s perspective, Citrix ADC appears to validate the credentials as usual with access being allowed or rejected and no visible change in behaviour.
Reversed-PIN (R-PIN) access
Behind the scenes, the initial login screen has a unique schema that uses a custom policy extension to reverse the user’s RADIUS PIN and attempt authentication using a RADIUS action that, on success, places the user into a custom default group. Citrix ADC then grants access if LDAP authentication access is successful.
In a typical situation, the RADIUS server would reject a reversed PIN, and success at this stage indicates the PIN was backwards when submitted by the user and a duress condition exists. By placing the user in a custom group, we can easily communicate the duress condition as Citrix ADC evaluates later policies.
Two responder policies bound to Citrix Gateway check for the group membership. The first calls a custom logging action that sends a definable text string to syslog and alerts the security team, the second places the username into an automatically expiring variable which prevents future logins from using PIN (rather than R-PIN) for four hours.
Session policies bound to the custom group in Unified Gateway’s AAA Groups feature now take precedence. As Smart Access supplies the session policy name to Citrix Virtual Apps and Desktops (formerly XenApp & XenDesktop) policies can now be triggered to disable critical applications or to place the user on a server with session recording enabled. If a decoy environment is preferred, then an alternative StoreFront server URL can be set in the session policy.
See https://support.citrix.com/article/CTX227055 for more details on SmartAccess
Standard PIN access
If R-PIN fails (and it should unless there is a duress condition) a schema to check the PIN in the correct order is applied, and one of two authentication policies trigger to check the PIN. The first policy processes the login as usual whereas the second rejects the login even if the credentials supplied are correct.
Allowing the standard PIN if R-PIN has not been used
The first authentication policy executes if the $slp variable is not set for the user and processes the login as usual. R-PIN use sets the $slp variable, and this check ensures that R-PIN has not used recently and a duress condition does not currently exist for the user.
Blocking the standard PIN if R-PIN has been used
The blocking policy executes if the username has been added to the $slp variable by a recent R-PIN login. This logic creates a time-based lock preventing PIN-based login for a definable period after R-PIN has been used and helps to prevent the duress system from being detected. In my example configuration, I expire the variable (which is acting as a timelock) after four hours.
To block logins the policy directs authentication to a non-existent LDAP server. Alternatively, the blocking logic could direct the user to the same authentication sequence used by R-PIN, set the same custom default group, and thereby cause the user to receive the same restricted access with the PIN forwards or backwards after R-PIN use and variable population.
Create the R-PIN policy extension
To check the R-PIN (reversed PIN), Citrix ADC must calculate it using the RADIUS PIN + OTP entered in the “passwd1” field of the login form. A new function is required to achieve this.
New functions can be created using Citrix ADC’s AppExpert Policy Extensions feature. Here, we create a text function called “RPIN” that reverses the first four characters of a string.
The function has been tested using standard RADIUS and challenge-response based authentication. The PIN is expected to be static and four characters in length. The user must be instructed not to use a palindromic number that is the same when written forwards or backwards (such as 1221).
Create the $slp variable used to disable PIN-based authentication after RPIN use
The $slp variable is a map that can contain up to 50 entries. Entries are set to expire if they are not referenced within 4 hours.
add ns variable slp -type “map(text(128),ulong,50)” -init 1 -expires 14400
A responder policy and associated action can now be created to populate the variable when a user in the custom “duress_group” completes a login.
add ns assignment activate_slp -variable “$slp[http.REQ.USER.NAME]” -set 1
add responder policy activate_slp_pol “HTTP.REQ.USER.IS_MEMBER_OF(\”duress_group\”)” activate_slp
bind vpn vserver UG_VPN_myUnifiedGateway -policy activate_slp_pol -priority 100 -gotoPriorityExpression NEXT -type REQUEST
Create the alarm message that will be recorded in Syslog
Custom Syslog messages can be generated using an audit policy and associated action that is triggered by requests made to the Unified Gateway vServer by users within the custom group.
add audit messageaction duress_event_activated EMERGENCY “\”ATTENTION DURESS CODE ACTIVATED BY \” + HTTP.REQ.USER.NAME “
add responder policy log_for_user_pin_pol “HTTP.REQ.USER.IS_MEMBER_OF(\”duress_group\”)” NOOP -logAction duress_event_activated
bind vpn vserver UG_VPN_myUnifiedGateway -policy log_for_user_pin_pol -priority 110 -gotoPriorityExpression NEXT -type REQUEST
The authentication vServer, nFactor profile, and authentication login can now be created.
Create authentication vServer, nFactor profile, and bind this to Unified Gateway
add authentication vserver auth SSL 0.0.0.0
bind ssl vserver auth -certkeyName “RSA Cert”
bind ssl vserver auth -eccCurveName P_256
bind ssl vserver auth -eccCurveName P_384
bind ssl vserver auth -eccCurveName P_224
bind ssl vserver auth -eccCurveName P_521
add authentication authnProfile DURESS -authnVsName auth -AuthenticationHost host
set vpn vserver UG_VPN_myUnifiedGateway -authnProfile DURESS
Create a login schema for the reversed-PIN, standard PIN, and for LDAP
# The xml schema file used here is built-in and included with Citrix ADC.
add authentication loginSchema dual_auth_using_RPIN -authenticationSchema “/nsconfig/loginschema/LoginSchema/DualAuth.xml” -passwdExpression “HTTP.REQ.BODY(1000).TYPECAST_NVLIST_T(\’=\’,\’&\’).VALUE(\”passwd1\”).RPIN”
add authentication loginSchema noschema_check_PIN -authenticationSchema noschema -passwdExpression “HTTP.REQ.BODY(1000).TYPECAST_NVLIST_T(\’=\’,\’&\’).VALUE(\”passwd1\”)”
add authentication loginSchema ldap_schema -authenticationSchema noschema -passwdExpression “HTTP.REQ.BODY(1000).SET_TEXT_MODE(URLENCODED).TYPECAST_NVLIST_T(\’=\’,\’&\’).VALUE(\”passwd\”).DECODE_USING_TEXT_MODE”
Create two RADIUS policies, one that sets a default duress group and another which does not
add authentication radiusAction radius_server_sets_group -serverIP RADIUS_SERVER_IP -serverPort 1812 -radKey 954f5d31b0ac689644c20cd9fdb874a058d7314374528545ea13dcc660995b0c -encrypted -encryptmethod ENCMTHD_3 -defaultAuthenticationGroup duress_group
add authentication Policy duress_policy -rule true -action radius_server_sets_group
add authentication radiusAction radius_server_wo_set_group -serverIP RADIUS_SERVER_IP -serverPort 1812 -radKey 2fe424b9e55ff6232676badf24aee365f4c6d9bfd82d73c30c92994984e665a1 -encrypted -encryptmethod ENCMTHD_3
add authentication Policy n_duress_radius_policy -rule true -action radius_server_wo_set_group
Create two LDAP authentication policies, one working and another that is broken
add authentication ldapAction AD_servers -serverIP LDAP_SERVER_IP -authTimeout 30 -ldapBase “dc=mycoolcompany,dc=local” -ldapBindDn firstname.lastname@example.org -ldapBindDnPassword 3baef4b867435f37d956171077caf623b4c1a568b8f4169872ef9b9943fdabc7
-encrypted -encryptmethod ENCMTHD_3 -ldapLoginName sAMAccountName -groupAttrName memberOf -subAttributeName CN -requireUser NO
add authentication Policy ldap_policy -rule true -action AD_servers
add authentication ldapAction fake_ldap_server -serverIP 127.0.0.1 -ldapBase broken -ldapBindDn broken -ldapBindDnPassword 1849c37fa390d63c6e768eb5c8e9438634e5088f79f1d613d954cde5e609ff60 -encrypted -encryptmethod ENCMTHD_3
add authentication Policy ldap_broken_policy -rule true -action fake_ldap_server
bind authentication vserver auth -policy dual_auth_using_RPIN -priority 100 -gotoPriorityExpression END
# Create nFactor logic
add authentication policylabel ldap_policylabel -loginSchema ldap_schema
bind authentication policylabel ldap_policylabel -policyName ldap_policy -priority 10 -gotoPriorityExpression NEXT
bind authentication vserver auth -policy duress_policy -priority 10 -nextFactor ldap_policylabel -gotoPriorityExpression NEXT
add authentication Policy noauth_policy -rule “http.req.url.contains(\”doAuthentication.do\”)” -action NO_AUTHN
add authentication policylabel n_duress_radius_policylabel -loginSchema noschema_check_PIN
bind authentication policylabel n_duress_radius_policylabel -policyName n_duress_radius_policy -priority 100 -gotoPriorityExpression NEXT -nextFactor ldap_policylabel
add authentication policylabel ldap_broken_policylabel -loginSchema noschema_check_PIN
bind authentication policylabel ldap_broken_policylabel -policyName ldap_broken_policy -priority 100 -gotoPriorityExpression NEXT
add authentication Policy allow_not_duress_login -rule “$slp.valueExists(http.req.user.name).not” -action NO_AUTHN
add authentication Policy block_not_duress_policy -rule “$slp.valueExists(http.req.user.name)” -action NO_AUTHN
add authentication policylabel not_duress_policylabel -loginSchema noschema_check_PIN
bind authentication policylabel not_duress_policylabel -policyName allow_not_duress_login -priority 10 -gotoPriorityExpression NEXT -nextFactor n_duress_radius_policylabel
bind authentication policylabel not_duress_policylabel -policyName block_not_duress_policy -priority 20 -gotoPriorityExpression NEXT -nextFactor ldap_broken_policylabel
bind authentication vserver auth -policy noauth_policy -priority 20 -nextFactor not_duress_policylabel -gotoPriorityExpression NEXT
Citrix TechBytes – Created by Citrix Experts, made for Citrix Technologists! Learn from passionate Citrix Experts and gain technical insights into the latest Citrix Technologies.
Want specific TechBytes? Let us know! email@example.com