最近いくつかのサイトをテストしました。最初にログインフォームがあり、パケットキャプチャを行ったところ、ユーザー名とパスワードが暗号化されていました。フォーマットは以下の通りです:
フロントエンドでよく使われる暗号化は aes で、aes のキーを rsa で暗号化し、その後 aes を使ってデータを暗号化して送信するケースもありました。
上の図は aes を直接使用した暗号化の例です。
ブラウザを使って暗号化された js ファイルを見つけることができます。一般的にはソースコード / ソースの中で js ディレクトリを見つけます。このサイトは比較的簡単で、crypto-js.js ファイルを直接使用しており、開くと aes のキーと暗号化方式がすぐに確認できます。
上の図から、キーが与えられ、モードは ECB、パディングは Pkcs7 であることがわかります。これらの情報があれば、データを解読することができます。
burpsuite のautodecoderプラグインを使用して、今回の解読もこのプラグインを使いました。
autoDecoder には 4 つのタブがあります。
最初の options タブは、いくつかの設定です。
- モジュール制御:burp のどのモジュールで autodecoder の暗号化 / 復号化を実行するか
- 暗号化 / 復号化オプション:自前のアルゴリズムを使うか、インターフェースを使うかを制御します。
後のオプションは最初のオプションに関連しています。たとえば、インターフェースを使った暗号化 / 復号化を選択した場合、データヘッダーを処理する
またはリクエスト応答の異なる暗号化/復号化
オプションを設定できます。
- 暗号化 / 復号化設定:暗号化ドメインを必ず記入する必要があります。そうしないと autodecoder タブに表示されません。他はデフォルトで構いません。
- 設定が完了したら、必ず
保存
してください。
2 番目のタブは autodecoder に自前のアルゴリズムによる暗号化 / 復号化があります。
3 番目のタブはインターフェースによる暗号化 / 復号化です。
インターフェースを設定する必要があります。インターフェースは作者が提供したものを参考にできます。リクエストパラメータをカスタマイズする必要がある場合は、ほとんどの場合インターフェースによる暗号化 / 復号化が必要です。
今回遭遇したように、2 つのパラメータが存在する場合、自前のアルゴリズムによる暗号化 / 復号化では要件を満たすことができません。
自前のアルゴリズムによる暗号化 / 復号化には正規表現パターンマッチングがあり、1 つの暗号化された文字列(name)のみをマッチさせることができ、pwd パラメータの暗号化された文字列を抽出することができません。
4 番目のタブはリクエスト応答の置き換えです。
権限昇格の脆弱性をテストするために使用でき、burp の Autorize プラグインと似ていると感じますが、使用したことはありません。
autodecoder プラグインについて簡単に紹介しました。
作者のスクリプトを少し変更して、現在テストしているサイトに適応させました。
from flask import Flask, request
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad,unpad
import base64
from urllib.parse import parse_qs,quote
import hashlib
def aes_encrypt(key, data):
cipher = AES.new(key, AES.MODE_ECB)
padded_data = pad(data.encode(), AES.block_size)
cipher_text = cipher.encrypt(padded_data)
return base64.b64encode(cipher_text).decode()
def aes_decrypt(key, data):
cipher = AES.new(key, AES.MODE_ECB)
decrypted_data = cipher.decrypt(base64.b64decode(data))
unpadded_data = unpad(decrypted_data, AES.block_size)
return unpadded_data.decode()
app = Flask(__name__)
@app.route('/encode', methods=["POST"])
def encrypt():
key = b'ここにaesのkeyを書く' # 16バイトのキー
str1 = '123'
param = request.form.get('dataBody') # POSTパラメータを取得
md5value = param + str1
param1 = quote(aes_encrypt(key,param))
param2 = hashlib.md5(md5value.encode()).hexdigest()
return f"Param={param1}&Autograph={param2}"
'''
data = json.loads(param)
encrypted_id = aes_encrypt(key, data["id"])
encry_param = param.replace(data["id"], encrypted_id)
return base64.b64decode(encry_param.encode()).decode()
'''
@app.route('/decode', methods=["POST"])
def decrypt():
key = b'ここにaesのkeyを書く'
param = request.form.get('dataBody')
print('param: ',param)
parsed_params = parse_qs(param)
print(parsed_params)
# 2つのパラメータを解読します。nameとpwdです。
decrypted_name = aes_decrypt(key, parsed_params["name"][0])
decrypted_value = aes_decrypt(key, parsed_params["pwd"][0])
# 2つのパラメータを含むJSON応答を返します。
return f"name={decrypted_name}&pwd={decrypted_value}"
if __name__ == '__main__':
app.debug = True # デバッグモードを設定します。運用時にはオフにすることを忘れないでください。
app.run(host="0.0.0.0", port=8888)
スクリプトを書いたので、実行します。プログラムは 127.0.0.1:8888 でリスンします。
python3 aed_decode.py
まずはパケットをキャプチャしてテストしてみてください。成功裏に解読されたことが確認できます。
現在の設定は以下の通りです:
設定が完了したら、パケットをキャプチャすると、各リクエストが自動的に解読されるのが確認できます。autoDecoder タブで解読内容を見ることができます。
また、burp の intruder モジュールを利用してログインブルートフォース攻撃を開始することもできます。
ここでは、非対称暗号、対称暗号、署名、再送信禁止のテストシナリオを含むフロントエンド暗号化対抗練習のための靶場をお勧めします。AES、DES、RSA など、ペネトレーションテストの練習に使用されます。