Misc - Simple Signing
Look at this signing service I made. I didnt want to implement my own hashing algorithm, so I just used python's. Nothing can go wrong right? - White
Challenge
from Crypto.Util.number import getPrime
from secrets import FLAG
ADMIN = b'adminTokenPlsNoSteal'
def sign(n: int, d: int, m: bytes):
if m == ADMIN:
print("no no")
exit(0)
h = hash(tuple(m))
return pow(h, d, n)
def verify(n: int, e: int, m: bytes, s: int):
h = hash(tuple(m))
return pow(s, e, n) == h
if __name__ == "__main__":
p, q = getPrime(1024), getPrime(1024)
e = 0x10001
n = p*q
d = pow(e, -1, (p-1)*(q-1))
print("""1. sign a message
2. get flag""")
while True:
inp = input("> ")
if int(inp) == 1:
msg = bytes.fromhex(input("Message to sign (in hex): "))
print("Signature:", sign(n, d, msg))
else:
sig = int(input("Give signature of admin token: "))
if verify(n, e, ADMIN, sig):
print("Congratz!!")
print(FLAG)
exit(0)
else:
print("You are not the admin")
exit(0)We are given the server file, which allows you to do to sign a message, and verify your signature.
The server allows you to sign any message other than the Admin token.
Let's look at how the signature is calculated
We need to give the signature of the Admin token inorder to get the flag
The signature scheme is similar to RSA Signing, except we sign a hash of the tuple of the message instead of the message itself.
So we need to find a collision for the hash or attack the signature scheme
Initially, I tried to find hashes for the factor of the hash, but since the factor of the hash is a large prime, it is equivalent of finding the corresponding hash for the prime which is the same as finding a collision
Therefore, we try to find a collision for the hash instead.
After searching for a long time, I was able to find this excellent writeup for a similar challenge (FCSC 2022). Here I will give a detailed description of that writeup.
FCSC 2022
The challenge is similar to the one given, except that it only allows ASCII values <128.
Since hashing a python integer is the same as taking mod , we don't need to worry about python object hashing for integers.
First, let's go through the Cpython source files for tuple hashing Objects/tupleobject.c
If we convert this to python, it looks something like this
Here, go is used instead of python because it's faster and uses less memory
The hash size is 64 bits, therefore we can use the birthday attack to try to find a collision after generating or hashes.
We can expect a collision if we construct hashes forward into a map A and hashes in backward into a map B in order to produce a 64-bit collision
However, this approach has significant time complexity, so we can relax the time complexity by constructing 4 forward rounds (4*7) + a partial round (1*3) for a total of hashes forward and 4 rounds in reverse (4*7) for a total of hashes backwards to cause a 56-bit collision.
Instead of directly storing the hashes at the end of forward round 5 and backward round 7, we do a partial round to speed up the attack by taking inverse with P2 and P1. We do this because
Store all the partial forward hashes of into A and into B. Then solve for in
To reduce the space complexity, we need to efficiently store (and index) the hashes. This is achieved using a clever bitset technique
Instead of storing all the bits of the forward rounds, we store 34 bits of the MSB by using 28 bits of the MSB as the index and remaining 6 bits of the MSB as the value. Since there can be multiple forward hash values having the same 28 bits of MSB, we use OR of 1 << (mid % 64) store the remaining 6 bits. In the backward rounds, if we want to check for possible collisions by checking the MSB, all we need to do is use the 28 bits of MSB as the index, and check if left shift of 8 bits of MSB is 1. This takes about 2 GB of RAM
Similarly for the backward round, we store 32 bits of the MSB by using 26 bits of the MSB as index and remaining 6 bits of the MSB as the value. However, we only store the backward hash values if the MSB of the forward round and MSB the backward round match. If they do match, we also store the 4 bytes of the backward round in a collisions map using 57 bits of the MSB instead of 64 bits as the index, because we still need to solve for the lower 7 bits which is in , where A and B are the forward and backward bitmaps.
Finally, we solve for . We already have the last 4 bytes in a collisions map, now we compute the forward round to get the values of and using the map B from the backward rounds, we can check for possible collisions by checking if the MSB of the forward and backward rounds match. If they match, then we check if the value exists in the collisions map, which we use as the index to find backward round bytes. Now that we have the forward round bytes and the backward round bytes , we solve for by hashing until it matches the hash of the challenge.
Full Implementation
After which we get the flag
Last updated