A simple script to install a certificate (specifically PKCS#12
) for the Zyxel GS1900 switch using the v2.80V2.80(AAHK.0) | 10/16/2023
firmware.
Firmware release notes can be found here: https://download.zyxel.com/GS1900-24E/firmware/GS1900-24E_2.80(AAHK.0)C0_2.pdf
The idea for it is to allow us to update the certificate automatically when needed (hands-free) with for example certbot.
After some investigation I found that the switch was very stubborn about how it wants the request sent.
In short:
- The form-data boundary requires a
----
prefix - The certificate needs to come before private key password.
The script is now fully working, allowing us to do what we set out to do.
A summary of how I figured all of this out can be found below.
Script usage:
python ./upload_cert.py <PATH_TO_CERTIFICATE>
Environment variables:
* URL Switch url (e.g.: https://switch.local)
* DISABLE_VERIFY_SSL In case SSL should be verified (default True)
* USERNAME Switch Username
* PASSWORD Switch Password
* KEY_PASSWORD Certificate Private Key Password
Currently it is in a proof-of-concept state, however authentication and retrieval of the CSRF token for uploading all works.
I am currently havning some issue getting it to recognize the certicate password or at least that's what I get from the response I'm getting: Upload certificate failed. Invalid SSL private key
.
I am not able to replicate the issue or the response in a browser as when I'm using the wrong password for a key I get the following response: Upload certificate failed. Invalid PKCS file.
I've tried inspecting the requests from both the browser and the script whereas the time of the request, the form boundaries and browser specific headers are the only differences.
Even after adding in some of the headers, it still reports the above error.
After banging my head at this for a few hours, I opened up Burp Suite and started looking at the differences between a browser request and Pythons requests library, I found that requests does not use the 4 ----
prefix to the form boundary which most browsers do, hence the Zyxel switch blankly refuses the request since it's not able to read the segments in the form.
Different form boundaries from the different browser types:
- Webkit browsers
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarySZM9ObnCj4YPwCwl
- Firefox:
Content-Type: multipart/form-data; boundary=---------------------------259477228521696883621690493288
- Python's requests:
Content-Type: multipart/form-data; boundary=675b3c098bec1d1719a567ae721f7f9e
After using Burp Suite to manipulate the request from my script until all I was left with was the form boundaries, I facepalmed.
I added one -
after another until I got a successful request, I found that 4 was the magic number (same as Webkit).
The following form boundary is accepted:
Content-Type: multipart/form-data; boundary=----DUMDUM
While the following is not (notice the missing -
):
Content-Type: multipart/form-data; boundary=---DUMDUM
Not only did the request need to have the correct boundary, the order of the form data is paramount.
However, the CSRF token etc is not (you still need to be authenticated).
So the requirement to update the certificate is for the form boundary to include the ----
prefix and that the certificate comes before the password in the body.
Once that's all prepared we can send the request and the certificate is updated on the switch.