Authenticated file upload to Remote Code Execution in Xerte

This is the third and final vulnerability found over a longer period of time in Xerte (for now?). This happened about a year after the previous vulnerabilities. Because of this, a lot of fixes and improvements had been applied to the project, making this vulnerability a lot harder and more fun to uncover! The vulnerability found was an authenticated file upload to Remote Code Execution (RCE).

“Xerte is an award-winning suite of browser-based tools that allow anyone with a web browser to create interactive learning materials quickly and easily.” – https://www.xerte.org.uk. Xerte is an OpenSource project and can be found at: https://github.com/thexerteproject/xerteonlinetoolkits.

PoC and discovery

When creating learning materials users have multiple ways of uploading files. One way is to upload files to the project as a whole, this was patched. But its also possible to upload files to a slide (much like a power point slide). This can be done via:

When inspecting this file upload in the chrome developer tools, we notice that a mediapath variable is present. The mediapath defines where the file is saved. This path is not validated and files can be saved anywhere on the server:

Naturaly the first thing to upload is an PHP webshell. The user’s media folder can’t execute php files due to htaccess, but other folders can. Sadly this is not allowed due to a decently big upload extension blacklist:

The blacklist contains the following extensions, a lot of useful extensions are blocked:

php,php5,pl,cgi,exe,vbs,pif,application,gadget,msi,msp,com,scr,hta,htaccess,ini,cpl,msc,jar,bat,cmd,vb,vbe,jsp,jse,ws,wsf,wsc,wsh,ps1,ps1xml,ps2,ps2xml,psc1,psc2,msh,msh1,msh2,mshxml,msh1xml,msh2xml,scf,lnk,inf,reg,docm,dotm,xlsm,xltm,xlam,pptm,potm,ppam,ppsm,sldm

Its important to note that: after uploading, the PHP function move_uploaded_file is used to move the file to the destination. The function move_uploaded_file will overwrite any existing file without question. What if the developers did a php include somewhere, where they forgot to add the .php extension to the file. Just overwrite the existing file and have the website include my file! All the include calls in the project where validated but all of them, rightfully so, included .php files.

While reading the code my eye caught a function called _load_language_file. Xerte is available in lots of different languages and language files use a .inc extension. These .inc files often include php code and .inc is not on the upload blacklist! Can we overwrite an existing .inc file with our php code and have it executed when a page is loaded?:

When loading the landing page the PHP in our .inc file is executed:

Time Line

  • 05/03/2021: Initial disclosure to vendor
  • 05/03/2021: Initial response from vendor
  • 06/03/2021: Vendor acknowledges the problem
  • 06/05/2021: Fix has been deployed, asked to verify fix
  • 07/05/2021: File upload still possible by path traversal (media path wrongly checked)
  • 10/05/2021: Fix has been deployed
  • 24/02/2022: CVE-2021-44664 assigned