Rating:

The hash function is reversable. With reversed sbox, p and hash function, we bruteforce each time for only 6 possibilities and win.

```
#!/usr/bin/env python3
from Crypto import Random
from Crypto.Random import random
from Crypto.Util.number import *
import itertools
import binascii
from pwn import *
import re

sbox = [221, 229, 120, 8, 119, 143, 33, 79, 22, 93, 239, 118, 130, 12, 63, 207, 90, 240, 199, 20, 181, 4, 139, 98, 78, 32, 94, 108, 100, 223, 1, 173, 220, 238, 217, 152, 62, 121, 117, 132, 2, 55, 125, 6, 34, 201, 254, 0, 228, 48, 250, 193, 147, 248, 89, 127, 174, 210, 57, 38, 216, 225, 43, 15, 142, 66, 70, 177, 237, 169, 67, 192, 30, 236, 131, 158, 136, 159, 9, 148, 103, 179, 141, 11, 46, 234, 36, 18, 191, 52, 231, 23, 88, 145, 101, 17, 74, 44, 122, 75, 235, 175, 54, 40, 27, 109, 73, 202, 129, 215, 83, 186, 7, 163, 29, 115, 243, 13, 105, 184, 68, 124, 189, 39, 140, 138, 165, 219, 161, 150, 59, 233, 208, 226, 176, 144, 113, 146, 19, 224, 111, 126, 222, 178, 47, 252, 99, 87, 134, 249, 69, 198, 164, 203, 194, 170, 26, 137, 204, 157, 180, 168, 162, 56, 81, 253, 213, 45, 21, 58, 24, 171, 37, 82, 53, 50, 84, 196, 232, 242, 244, 64, 80, 10, 114, 212, 187, 205, 28, 51, 182, 16, 107, 245, 211, 85, 92, 195, 5, 197, 200, 31, 183, 61, 123, 86, 167, 154, 41, 151, 35, 247, 246, 153, 95, 206, 149, 76, 112, 71, 230, 106, 188, 172, 241, 72, 156, 49, 14, 214, 155, 110, 102, 116, 128, 160, 135, 104, 77, 91, 190, 60, 42, 185, 96, 97, 251, 218, 133, 209, 65, 227, 3, 166, 255, 25]
p = [5, 9, 1, 8, 3, 11, 0, 12, 7, 4, 14, 13, 10, 15, 6, 2]
round = 16

sbox_rev = [47, 30, 40, 252, 21, 198, 43, 112, 3, 78, 183, 83, 13, 117, 228, 63, 191, 95, 87, 138, 19, 168, 8, 91, 170, 255, 156, 104, 188, 114, 72, 201, 25, 6, 44, 210, 86, 172, 59, 123, 103, 208, 242, 62, 97, 167, 84, 144, 49, 227, 175, 189, 89, 174, 102, 41, 163, 58, 169, 130, 241, 203, 36, 14, 181, 250, 65, 70, 120, 150, 66, 219, 225, 106, 96, 99, 217, 238, 24, 7, 182, 164, 173, 110, 176, 195, 205, 147, 92, 54, 16, 239, 196, 9, 26, 214, 244, 245, 23, 146, 28, 94, 232, 80, 237, 118, 221, 192, 27, 105, 231, 140, 218, 136, 184, 115, 233, 38, 11, 4, 2, 37, 98, 204, 121, 42, 141, 55, 234, 108, 12, 74, 39, 248, 148, 236, 76, 157, 125, 22, 124, 82, 64, 5, 135, 93, 137, 52, 79, 216, 129, 209, 35, 213, 207, 230, 226, 159, 75, 77, 235, 128, 162, 113, 152, 126, 253, 206, 161, 69, 155, 171, 223, 31, 56, 101, 134, 67, 143, 81, 160, 20, 190, 202, 119, 243, 111, 186, 222, 122, 240, 88, 71, 51, 154, 197, 177, 199, 151, 18, 200, 45, 107, 153, 158, 187, 215, 15, 132, 249, 57, 194, 185, 166, 229, 109, 60, 34, 247, 127, 32, 0, 142, 29, 139, 61, 133, 251, 48, 1, 220, 90, 178, 131, 85, 100, 73, 68, 33, 10, 17, 224, 179, 116, 180, 193, 212, 211, 53, 149, 50, 246, 145, 165, 46, 254]
p_rev = [6, 2, 15, 4, 9, 0, 14, 8, 3, 1, 12, 5, 7, 11, 10, 13]

def pad(data, size = 16):
pad_byte = (size - len(data) % size) % size
data = data + bytearray([pad_byte]) * pad_byte
return data

def repeated_xor(p, k):
return bytearray([p[i] ^ k[i % len(k)] for i in range(len(p))])

def bytes_to_int(xbytes):
return bytes_to_long(xbytes)

def int_to_bytes(x):
return long_to_bytes(x, 16)

def group(input, size = 16):
return [input[i * size: (i + 1) * size] for i in range(len(input) // size)]

def hash(data):
state = bytearray([208, 151, 71, 15, 101, 206, 50, 225, 223, 14, 14, 106, 22, 40, 20, 2])
data = pad(data, 16)
data = group(data)
for roundkey in data:
for _ in range(round):
state = repeated_xor(state, roundkey)
for i in range(len(state)):
state[i] = sbox[state[i]]
temp = bytearray(16)
for i in range(len(state)):
temp[p[i]] = state[i]
state = temp
return hex(bytes_to_int(state))[2:]

def hash_rev(state, roundkey):
if len(state) % 2 == 1:
state = '0' + state
state = bytearray(binascii.unhexlify(state))
for _ in range(round):
temp = bytearray(16)
for i in range(len(state)):
temp[p_rev[i]] = state[i]
for i in range(len(state)):
temp[i] = sbox_rev[temp[i]]
temp = repeated_xor(temp, roundkey)
state = temp
return hex(bytes_to_int(state))[2:]

def gen_commitments():
secret = bytearray(Random.get_random_bytes(16))
rc = hash(secret + b"r")
pc = hash(secret + b"p")
sc = hash(secret + b"s")
secret = hex(bytes_to_int(secret))[2:]
rps = [("r", rc), ("p", pc), ("s", sc)]
random.shuffle(rps)
return secret, rps

def check_win(a, b):
if a == "r":
if b == "p":
return True
else:
return False
elif a == "s":
if b == "r":
return True
else:
return False
elif a == "p":
if b == "s":
return True
else:
return False
return False

def main():
r = remote("crypto1.ctf.nullcon.net", 5000)
for i in range(20):
txt = r.recvuntil("Your move:")
print(txt)
m = re.search("my move: ([0-9a-f]{31,32}) ([0-9a-f]{31,32}) ([0-9a-f]{31,32})\nYour move:", txt.decode())
rps = []
rps.append(m.group(1))
rps.append(m.group(2))
rps.append(m.group(3))
print(rps)
inp = ""
for item in itertools.permutations([0, 1, 2], 3):
hashr = hash_rev(rps[item[0]], pad(b"r"))
hashp = hash_rev(rps[item[1]], pad(b"p"))
hashs = hash_rev(rps[item[2]], pad(b"s"))
if hashr == hashp == hashs:
if item[0] == 0:
inp = "p"
elif item[1] == 0:
inp = "s"
else:
inp = "r"
break
r.send(inp + "\n")
print(r.recv(4096))

if __name__ == '__main__':
main()

```