Zentao Pro 8.8.2 RCE

Adam C
7 min readSep 23, 2020

In July, a remote command execution exploit was released on an application named Zentao Pro. When I examined the application’s code, I found another area which could be exploited to gain RCE. I informed the developers via Facebook messenger and email on July 16th 2020. This exploit has only been tested on Zentao Pro 8.8.2 and 8.8.3. The original exploit is on exploit-db.

So to recap, we have a code review methodology which is a cycle of planning, reviewing, reassessing. You can see this on my other blog. This one is aimed to demonstrate the methodology so you can see how it can be applied to find the RCE I found.

Understanding the application

During the initial stages of the code review, it is critical to understand more about how the application works and what possible attack vectors there may be. The initial questions were:

  • What is Zentao Pro used for?

It is a Project Management Tool to support software development. It keeps tracks of sprints, tracks bugs, creates charts for statistics. You could compare it with other software such as Jira.

  • What technologies are used in the application?

The language used is PHP, the database is MySQL. The code base was really easy to read so I didn’t look further into it.

  • What features are there?

To do this, there are two ways of discovering the features. You can look through the code, this may allow you to find hidden features. The other way is to use the application and wiki to find as many use cases as possible. Some interesting features to note are;

+ Export Calendar,
+ Multiple File Upload Locations
+ Creating Custom SQL queries
+ LDAP integration
+ Integrating SVN and Git
+ Cronjobs.

  • Are there any sensitive functions in the application?

This was quite simple, just look for sensitive functions such as system(), exec(), passthru(). There are other functions too.

Searching for sensitive functions (passthru)
Searching for sensitive functions (exec)

After the initial cycles, my understanding of the application has greatly increased. At this point, I have an idea of different attack vectors that can be used.

Trying out different attack vectors

The next cycles in my methodology focus more on investigating different use cases and finding out if I can deviate from the expected behavior. To do this, it is critical to further my understanding of the finer details of the application. This is what was in my list of things to-do:

  • Investigate how the SQL Query works. (SQL Injection?)
  • Investigate how the file upload functionality works. (Insecure file upload / Directory traversal?)
  • Investigate the cronjob section (RCE?) Here I explain how to work from the view to find the vulnerable code in the back-end.
  • Investigate the sensitive functions found in the previous round (This is working backwards from a sensitive function to a starting point)

SQL Query Investigation

To identify the area in the code for the SQL query functions, I interacted with the web application to attempt basic SQL Injection queries. I found that some of them got spaced out. To understand how this happens I have to look at the code.

I found that modules in the application are stored under the directory “xampp\zentaopro\module”. As the SQL Query section is under reports, I found a folder named: “xampp\zentaopro\module\report”. After reading the files, I found that the application has a list of blacklisted SQL commands. This sucks for us if we are attempting SQL injections.

Spaced out suspicious statements.
Zentaopro\module\report\ext\config\crystal.php shows there is an SQL blacklist

However, this doesn’t explain why the commands get spaced out. To try figure this out, I went back to the web application, submitted the query and intercepted the request using burp. Using this technique, I find out the command is sent to “/pro/report-ajaxCheckVar-0-.html”.

The “ajaxCheckVar” looks like a function so I decided to try find it in the files. We can see there are 3 areas where the ajaxCheckVar is used. On first inspection we can note the following.

  • The first function is a js file which runs when the query button is clicked.
  • The second function is the view which would then call the controller.
  • The third file just declares some default values.
ajaxCheckVar is used in these instances.

The second instance has the highest chance of showing us the flow of executing the SQL query. The inlink function is declared in another file and I followed the code.

After some time, I found this function was more complicated than I expected and to make sure I don’t go into a rabbit hole, I decided to investigate this in a separate ‘cycle’ and put it to the side.

Investigating the inlink function

We have also learned from this cycle is that the developer likes to use blacklists. From this knowledge, I decided to check what other blacklists there were by searching for the string “passthru” as we are interacting with PHP. As a result, I also found an array named ‘$evils’ which contains multiple strings useful for RCE.

The evils array

File Upload Investigation

After investigating the SQL Query section of the application. The to-do list / master list was be updated appropriately. To investigate the file upload functionality, I used my previous knowledge of how the files are structured to find the relevant code.


In this directory we can see there is a file named model.php and a function called create which creates the new document and stores it in a database.

The file is stored on the database.

At this stage, I did not know what the dao part of the line did. So I just did some quick research on it and found it just helps the code interact with different kinds of database with the same code.

So now there are 2 options; continue investigating the file functionality or look more into how files are retrieved etc. in a cycle later on.

To prevent myself going into a rabbit hole, I wrote down a couple of theories I had for exploiting this upload file functionality and moved on. The key point I learned here was that the files are uploaded into a database.

Investigating the cronjob

The application allows for users to create cron jobs. To find the code related to this functionality, you can search through the directories or use the application and find it from there.

In this case, I used the application to find the related code. I created a new cronjob and intercepted the POST request. The value for the cron is saved as ‘command’ and using that keyword I found useful code in


The POST request’s body.
The related code.

After identifying the key points where commands are executed, I worked my way backwards to look for any sanitation. In this instance there was none and I was able to gain remote code execution through this functionality.

Remote code execution on the cronjob section of the application

Investigating the sensitive functions

Earlier in this review, we looked for any functions that can be used to perform remote code execution. One of the files that were found was \xampp\zentaopro\bin\php\crond.php. You can use this as a starting point to find the new RCE in the cronjob section of the application.

The other interesting file is: \xampp\zentaopro\lib\scm\scm.class.php. In this PHP file there is a function named “execCmd” which contains the line
passthru($cmd, $result); This is the vulnerability published on exploit-db. You can have a try at this yourself.

The original RCE exploit.


To find my new RCE vulnerability these are some key learning moments that helped me:

  • Be aware of potential rabbit holes. If the cycle’s goal is achieved, reset and write everything you’ve learned down. Then when venturing down different rabbit holes, you will be more refreshed and you will also waste less time. For example, I could’ve spent a lot more time understanding how the SQL Query spaces everything out. However, once I understood there was little to be gained I decided that I would do it later.
  • Look EVERYTHING up. During this review, there were some bits I did not know such as PHP’s “dao”. This enabled me to understand the application a lot better when reading the code. Anything that pings some sort of curiosity should be investigate.
  • There are multiple ways of finding relevant code. You can start from browsing the code or start from finding variables in the application’s requests. In some cases, I worked from both ends to meet in the middle to fully understand the flow of the application.
  • Learning the habit’s of the developer really helped me find out the blacklisted SQL functions quickly.

Thanks for reading