Lepide AD Self-Service (LADSS) is vulnerable to a forced browsing issue that allows an unauthenticated actor to download an encrypted backup, however the backup is encrypted with a static key that can be extracted from the application code.
Successful decryption gives the actor access to cleartext credentials which can be used to access the administrative interface of ADSS. Once logged-in the actor can abuse a command injection vulnerability in the "SMS Gateway settings" resulting in Remote Command Execution as NT AUTHORITY\SYSTEM.


This blog post discloses 2 individual bugs that are being chained in order to achieve unauthenticated remote command execution. This command execution is most likely going to happen directly on a Active Directory Domain Controller or on a server with a Technical AD account with privileged access. Furthermore, this remote execution occurs with the highest possible local privileges possible namely as “NT AUTHORITY\SYSTEM”. Resulting in a remote compromise of an organizations AD.
Below both vulnerabilities are explained separately and finally combined in the final PoC at the end.

Unauthenticated File/Backup Download

The Lepide web application runs on a Tomcat webserver with a .war file deployed onto it. As there is no obfuscation or any protection against reverse engineer the application, it’s fairly easy to the browse through the source code.
As my common approach I started to look at the different functions and looked how the authentication or the sessions checks are done. I’ve discovered numerous functions that are missing or have the checks at the wrong place.
One of those function would be the creation of a backup that could be imported afterwards.
When you analyze/extract the “ROOT.war” file which is basically the web application archive of the Lepide Active Directory Self Service (LADSS) web application. The vulnerable code is located at “com.lepide.action.EnrollmentNotificationAction.class”.


The function that is ran is conveniently named “Backup” and as you can see there is no mention of a check being done whether this request was done through an active/authorized session.
Comparing this with other functions in the same class we can see that on those the session is indeed validated before proceeding to perform the action. Such as “Restore Database” or “Restore Backup”.


We could easily confirm this by performing the backup HTTP request without an active session (no cookies) as demonstrated below:


However, with the backup file itself we’re not quite there yet. It seems this backup file is encrypted, when trying to open .zip file with a archive manager the user is prompted with a “file corrupted” message.

In order to understand how this archive file is made up we need to dive deeper in the code of the “backup” function.


As seen above the zip archive stream is sent to the** “encrypt” function** with a key “piechidelta”.


Looking inside the “encrypt” function we can conclude the following:

  • ZIP archive is encrypted using DES with key “piechidelta”
  • Database contents are encrypted with AES with key “TheBestSecretKey”

We can reuse the code available to achieve decryption of the archive and the database content inside the ZIP archive.
NOTE: Database contents in Lepide AD Self-Service 2018 and older are not encrypted!

Once decryption is complete we can extract the following info from the database:

  • Admin login credentials for Lepide AD Self-Service Portal
  • Administrative AD user credentials that can update/reset AD passwords (usually domain admin credentials, not best practice, but most common)
  • Domain controller IP address

Now we have administrative credentials to the portal as well as AD administrative credentials, this still doesn’t directly allow us to execute system commands.
Now this is where vulnerability number 2 comes in.

Command Injection

I started looking inside the code if there were functions that would take user controlled input without sanitization. We found the function “send_test_sms”.


The function takes the value of the HTTP GET parameter “prop” and put it in “strUserParameters”. It then continues to use “<<;>>” as the delimiter to split the value inside the “prop” parameter and stores it in an array.
If the first value in the array matches “GSM Modem” or “SMS Gateway” it will use those code blocks respectively, however if none of it matches it will assume run a different code block.
This code blocks is executed when the first value in the “props” array doesn’t match any given statement. It will continue to assume we would like to run a custom executable (props[1]) and a its given command line parameters (props[2]) and calls the function “runExecutableFile”.


The “runExecutableFile” function is quite short and takes a String array and a message, all variables are directly controllable and not sanitized nor validated before being used in a command line. If the extension of the user provided filename ends with a .jar then Java is used to execute to run the command.
Once we have this it quite easy to craft our payload and have it execute our own executable and our own command line arguments. We simply use path traversal “../” to move to our desired location.

The final HTTP GET request looks as follows:

http://<LADDS_ADDRESS>/<<;>>../../../../../../../Windows//SysWow64//cmd.exe<<;>>/C whoami & <<;>>

Putting it all together (PoC)

Exploit code can be found here.


The following workarounds have been identified that would decrease the likelihood of a possible attack:

  • Block access via WAF to http://yourLADSS/
  • Hide your LADSS behind a VPN and avoid exposing it publicly


This vulnerability is being disclosed publicly without a patch in accordance with the ZDI 120 day deadline.

  • 10/08/20 – ZDI reported the vulnerability to the vendor/ICS-CERT
  • 02/10/21 – ZDI notified the vendor of the intention to publish the case as a 0-day advisory on 02/18/21

More information here.


Thanks to my collegue Eric Schayes (@EricSchayes) for working on this with me and thanks to Zero Day Initiative.