Generating TOTPs for CyberArk Privileged Session Manager Webapp Connectors using a PreConnect DLL

With version 13.0 of CyberArk's Privileged Session Manager, as part of a Webapp PSM connector, you can inject a time-based one-time password. You can even store the secret for the TOTP in a separate account and link it as a logon account to secure it.

As the latest LTS version is 12.6, many customers are 'stuck' without TOTP injection support but by using a custom PreConnect DLL for Webapp PSM connectors -- a feature introduced in PSM version 12.2-- we can achieve the same functionality and have TOTP injection with PSMs as early as version 12.2

What is a PreConnect DLL?

A PreConnect DLL is a piece of code that is run before a Webapp PSM connector's WebFormFields is executed. Account properties can be passed to the DLL as parameters and return values can be used in the connector's WebFormFields' input fields. CyberArk provides straightforward documentation on how to develop and configure a PreConnect DLL.

Creating the PreConnect DLL

We will keep the PreConnect DLL simple.

It will take a single parameter -- logonaccount_password -- being the TOTP secret, generate a six-digit TOTP using it, and return the TOTP. We will use &MfaCode& to represent the TOTP for use in the WebFormFields.

As version 12.2 of the PSM comes with Otp.NET out of the box in the Components folder, we will re-use the library to do the OTP generating and leave our PreConnect DLL to handle just the receiving of the parameter and the returning of the TOTP. With this, we have only a single file we need to distribute to each PSM.

using CyberArk.PSM.WebAppDispatcher.PreconnectUtils;
using OtpNet;
using System;
using System.Collections.Generic;
using System.Net;
using System.Security;
using System.Text;

namespace CyberArk.PSM.GenerateTOTPPreconnect
{   

    public class GenerateTOTP : IPreconnectContract
    {
        private readonly string TOTP_OUTPUT_PARAMETER = "MFACode";

        public Dictionary<string, SecureString> GetParameters(Dictionary<string, SecureString> parameters, LogUtils.WriteToLogHandler writeToLogMethod)
        {
            string totpCode;

            try
            {
                byte[] totpSecretBytes = Encoding.UTF8.GetBytes(new NetworkCredential(string.Empty, parameters["logonaccount_password"]).Password);

                writeToLogMethod("Generating TOTP.", Consts.LOG_LEVEL_INFO);

                var totp = new Totp(totpSecretBytes);
                totpCode = totp.ComputeTotp();
            }
            catch (KeyNotFoundException ex)
            {
                writeToLogMethod(string.Format("Key not found in parameters dictionary: {0}", ex.ToString()), Consts.LOG_LEVEL_ERROR);
                throw new PreconnectException("Key not found in parameters dictionary");
            }
            catch (Exception ex)
            {
                writeToLogMethod(string.Format("Error while generating TOTP: {0}", ex.ToString()), Consts.LOG_LEVEL_ERROR);
                throw new PreconnectException("Error while generating TOTP");
            }

            writeToLogMethod("Successfully generated TOTP.", Consts.LOG_LEVEL_INFO);

            return new Dictionary<string, SecureString> {
                {
                    TOTP_OUTPUT_PARAMETER,
                    new NetworkCredential(string.Empty, totpCode).SecurePassword
                }
            };
        }
    }
}
Note: I am not a C# developer so there is room for improvement!

After compiling the DLL and moving it to the Components folder of our PSM, we are ready to leverage it with the Webapp PSM connector.

You can find the code and Visual Studio project, including a compiled DLL, in my GitHub repository below:

https://github.com/aaearon/CyberArk.PSM.GenerateTOTPPreconnect

Configuring the Webapp PSM Connector

💡
This was tested in a PAM self-hosted environment with Vault version 13.0 and PSM 12.2. The 12.2 PSM is the second PSM installed, after one running version 13.0.

With the compiled DLL copied to all PSMs,

  1. Ensure that the Webapp PSM connector has LogonAccount added under Component Parameters > Target Settings > Supported Capabilities.

  2. Add the required parameters PreConnectDllName and PreConnectParameters as described in step three. For PreConnectDllName, specify the name of the DLL (CyberArk.PSM.GenerateTOTPPreconnect.dll if downloaded from my GitHub repository) and for PreConnectParameters provide just logonaccount_password.

    a screenshot of the PreConnectDllName parameter

    a screenshot of the PreConnectParameters parameter

  3. In WebFormFields, use &MfaCode& to inject the TOTP in the desired input field.

Connecting with our Webapp PSM connector, the TOTP is generated based on a secret that is stored as the password for the account's linked logon account.

a screenshot showing the generated TOTP being injected

The code and a compiled DLL can be found at https://github.com/aaearon/CyberArk.PSM.GenerateTOTPPreconnect.