q9.pcap
Wiresharkでみてみる
タイトルの通り、ダイジェスト認証らしい
ダイジェスト認証の手順
- クライアントからの初期リクエスト: クライアントは最初に認証が必要なリソースへのリクエストを送信します。
- サーバーからの認証要求: サーバーは401ステータスコードとともに「WWW-Authenticate」ヘッダを送信します。このヘッダには、ノンス(一度きりの値)やその他の認証に関連する情報が含まれます。
- クライアントの応答: クライアントはユーザー名とパスワードを使用していくつかの応答パラメータを計算します。以下は、一般的な計算のステップです:
- HA1の計算:
HA1 = MD5(ユーザー名 ":" レルム ":" パスワード)
- HA2の計算:
HA2 = MD5(HTTPメソッド ":" リクエストのURI)
- 応答の計算:
応答 = MD5(HA1 ":" サーバーからのノンス ":" ノンスカウント ":" クライアントノンス ":" QoP ":" HA2)
HA1 | MD5(ユーザー名 ":" レルム ":" パスワード) |
HA2 | MD5(HTTPメソッド ":" リクエストのURI) |
応答 | MD5(HA1 “:” サーバーからのノンス “:” ノンスカウント “:” クライアントノンス “:” QoP “:” HA2) |
手順
GET /q9/ のレスポンスを分解する
response=”e9654c012dc42f9f78f81a685073df98″ からMD5逆算すると
c627e19450db746b739f41b64097d449:HHj57RG8BQA=4714c627c5195786fc112b67eca599d675d5454b:00000003:1064eaa9478a0396:auth:a2baed8041744d5c3d6cc250222d5930
MD5デコード結果 | ||
---|---|---|
| c627e19450db746b739f41b64097d449 | 不可 |
サーバーからのノンス | HHj57RG8BQA=4714c627c5195786fc112b67eca599d675d5454b | |
ノンスカウント | 00000003 | |
クライアントノンス | 1064eaa9478a0396 | |
QoP | auth | |
HA2 | a2baed8041744d5c3d6cc250222d5930 | GET:/q9/htdigest |
※HA1をMD5 decryptはできませんでした(=パスワードを取得できない)
つまり、(パスワードは不明のままでも)ダイジェスト認証を通すためには、上記が一致していれば認証が通るということです。(つまり、ブラウザからダイジェスト認証を通すのではなく、リクエストヘッダを意図的に生成してやればよいということ)
ここで一度パケットを確認する
どうやら、フラグは flag.html にあるそうです。
Pythonでリクエストを指定して取得してみよう
ハッシュ変換前の値、補足 | ||
---|---|---|
| c627e19450db746b739f41b64097d449 | 不明 |
サーバーからのノンス | ? | 通信時にサーバーが生成する |
ノンスカウント | 00000001 | 多分1でよい? |
クライアントノンス | ? | こちらが指定する=なんでもいい |
QoP | auth | |
HA2 | 9e2b6bca5d4d92f6ead358623df264c8 | GET:/q9/flag.html |
import requests
from requests.auth import HTTPDigestAuth
import hashlib # hashlibモジュールをインポート
import re
# 初期変数の設定
url = "http://ctfq.u1tramarine.blue/q9/flag.html"
username = "q9"
realm = "secret"
uri = "/q9/flag.html"
qop = "auth"
nc = "00000001"
cnonce = "your_cnonce_here" # クライアントが生成する一意の値
HA1 = "c627e19450db746b739f41b64097d449"
HA2 = "9e2b6bca5d4d92f6ead358623df264c8"
# 最初の未認証リクエストを送信してnonceを取得
response = requests.get(url)
if 'www-authenticate' in response.headers:
auth_header = response.headers['www-authenticate']
nonce = re.search('nonce="([^"]+)', auth_header).group(1)
else:
print("WWW-Authenticate header not found")
exit()
# レスポンスの計算
response_value = hashlib.md5(f"{HA1}:{nonce}:{nc}:{cnonce}:{qop}:{HA2}".encode()).hexdigest()
# カスタム認証ヘッダーを作成
auth_header = f'Digest username="{username}", realm="{realm}", nonce="{nonce}", uri="{uri}", qop={qop}, nc={nc}, cnonce="{cnonce}", response="{response_value}"'
# 認証リクエストを送信
response = requests.get(url, headers={'Authorization': auth_header})
# レスポンスを表示
print(response.status_code)
print(response.text)
まとめ
- ダイジェスト認証の仕組みを理解した
- pythonで指定したヘッダを送ることができることを理解した
コメント