Skip to main content
  1. Posts/

Fortigate - Recover IPSec VPN PSK

During a work related project I needed a method for extracting the Pre-shared key of a VPN tunnel in plain-text from a Fortigate firewall. However, this isn’t straightforward as you can’t view the configured PSK in the web interface.

Even if you have a look at the VPN configuration on the CLI the Fortigate displays the PSK only in an encrypted form. This can be seen in the following excerpt where the ENC keyword indicates that the value is encrypted.

Fortigate-VM # show vpn ipsec phase1-interface
config vpn ipsec phase1-interface
    edit "Vladilena"
        set interface "port1"
        set peertype any
        set net-device disable
        set proposal aes128-sha256 aes256-sha256
        set comments "Test VPN Tunnel"
        set wizard-type static-fortigate
        set remote-gw 192.168.178.54
        # PSK is only displayed as an encrypted value
        set psksecret ENC KQdvafObUI...
    next
end

However, it is possible to retrieve the plain-text key over the REST API of the firewall. First login to the Fortigate with the following curl command. Make sure to adjust the IP address as well as the user credentials to your system.

$ curl -k -X POST "https://172.21.130.109/logincheck" \
  -d "username=admin&secretkey=12345" -c cookies.txt

If the login is successful curl should output something like this:

<script language="javascript">
document.location="/prompt?viewOnly&redir=%2F";
</script>

Additionally a ccsrftoken as well as an APSCOOKIE value should have been saved to the specified cookies file.

One the login is done you can access the VPN configuration of the device over the following API entry point: https://x.x.x.x/api/v2/cmdb/vpn.ipsec/phase1-interface?plain-text-password=1. The plain-text-password=1 parameter makes sure that the firewall responds with the plain-text password instead of an encrypted value.

The JSON response from the Fortigate is rather lengthy as it contains the full VPN configuration including default values. It’s therefore advised to filter the result with tools such as jq or grep. The relevant field that contains the PSK of the VPN tunnel is called psksecret.

I only had one single VPN tunnel configured on my device, hence I used jq to extract the information. This can be seen in the following example:

$ curl -s -k -b cookies.txt \
  "https://172.21.130.109/api/v2/cmdb/vpn.ipsec/phase1-interface?plain-text-password=1" \
  | jq -r ".results[0] .psksecret"
DummySecretKey86

The result DummySecretKey86 is the plain-text PSK that was configured during tunnel creation.

If you operate your system in Multi-VDOM mode and need to extract the VPN configuration of a specific VDOM add the URL parameter vdom=VDOM-NAME to the GET request.

Alternatively it’s possible to send the API requests with one of many programming languages. The following piece of Python code uses the requests library to achieve the same goal as the previous curl commands.

import requests

session = requests.Session()

credentials = {"username":"admin", "secretkey":"12345"}

session.post(
    url="https://172.25.86.20/logincheck",
    data=credentials,
    verify=False,
)
result = session.get(
    url="https://172.25.86.20/api/v2/cmdb/vpn.ipsec/phase1-interface?plain-text-password=1",
    verify=False
)
print(result.json()["results"][0]["psksecret"])

Using the plain-text-password=1 API parameter also works for other configurations. For example it’s possible to retrieve configured RADIUS passwords this way. However it’s not possible to obtain passwords of local users as the firewall only stores a hash value instead of the plain-text password.