BsBsこうしょう

これは考えたことではなく思ったことです。

Salt and Pepper[writeup][Crypto CTF 2021]

問題

  • システムにログインしてね
  • usernameは n3T4Dm1n、passwordは P4s5W0rd
  • ただしそのハッシュも一緒につける必要があるよ
  • ハッシュは sha1(salt + password + md5(pepper + username)) だよ
  • saltとpepperのハッシュと文字数は分かっているよ

解説

salt、pepperがハッシュ関数で前についていることから、このハッシュにはLendth Extension Attack(身長攻撃)が可能。しかし、ややこしく本当にできるかどうかは疑問が残る(できます)。

伸長攻撃についてはこちら a8pfactory.hatenablog.com

このハッシュ関数において、自由に操作できる文字列はpasswordとusername。md5()の部分が操作できないため難しいように見えるが、(伸長攻撃が salt + originalMessage + padding + addMessage のハッシュを 事前に 求められることに注目すれば、)md5を事前に求めておくことができるため addMessage = password + md5hash とし、後でmd5hashを取り除くことで伸長攻撃が可能である。

では実装するだけ……と思いきや、伸長攻撃ツールhashpumpはoriginalMessageの長さが0の場合、エラーを吐いてしまうため、うまく実装できない。そこで、saltの最後の1byteをoriginalMessageとし、その1byteを全探索する。もちろん最終的に得られるmessageには余計なものがあるので取り除く。md5sha1でそれぞれ調べる必要があるので、全体で65536回くらいの試行が必要となる。実装したものが以下である。

from pwnlib.util.packing import pack
import hashpumpy
import pwn
md5salt = '5f72c4360a2287bc269e0ccba6fc24ba'
sha1pepper = '3e0d000a4b0bd712999d730bc331f400221008e0'
saltpepper = 19

USERNAME = b'n3T4Dm1n'
PASSWORD = b'P4s5W0rd'
f = False

for s in range(0,256):
    for p in range(0,256):
        md5digest, user = hashpumpy.hashpump(md5salt, s.to_bytes(1, 'big'), USERNAME, saltpepper-1)
        sha1digest, word = hashpumpy.hashpump(sha1pepper, p.to_bytes(1, 'big'), PASSWORD+md5digest.encode(), saltpepper-1)
        socket = pwn.remote('02.cr.yp.toc.tf', 28010)
        socket.recvuntil('uit')
        socket.sendline('l')
        socket.recvuntil('comma: ')
        name = hex(int.from_bytes(user[1:], 'big'))[2:] + ',' + hex(int.from_bytes(word[1:-32], 'big'))[2:]
        socket.sendline(name)
        socket.recvuntil('hash: \n')
        socket.sendline(sha1digest)
        packet = socket.recv(1024)
        # print(packet)
        if b'flag' in packet:
            f = True
            socket.close()
            break
        socket.close()
    if f:
        break

これでFLAGを得ることができた。

疑問点

ところでこのプログラムを動かすと、すべてのケースでFLAGを獲得できることが分かった。なぜだろう?

実際に伸長攻撃ライブラリを動かしたことがある人ならお分かりかもしれないが、addMessageを変えない場合、saltの文字数を変えてもライブラリが出力するハッシュは変わらない。変わるのはmessageだけだ。なぜならば、saltの文字情報がすべて含まれている既知ハッシュの情報は何も変わらないためである。

今回のケースもこれと同じことが起きている。saltの最後の文字を全探索して、saltの文字数を1文字引いたとしても、saltの情報が変わる=既知ハッシュが変わるわけではない。既知ハッシュが変わらないということは、うまく辻褄を合わせられる範囲であれば、 文字数と既知ハッシュさえ合っていれば伸長攻撃ライブラリ は正しく動作するということになる。

これが個人的に非自明だったので、今回このwriteupを残した。