# Omni Crypto Writeup## Pwn2Win CTF 2020 Crypto 246 points 32 solves
### Observations
We discovered that Primes generated by the getPrimes method in the challengeAlways generates primes that are symmetric around one of the modular square root of N modulo 2^1024.
We know that N = p*q --> N = (a+b)*(a-b) <=> N = a^2 - b^2 from fermat's factorization methodThus, b^2 = a^2 - N.
The choosen root shall always satisfy the condition root^2 > N. 
Thus, we can have a = root, and b as the deviation from a to give p and qThus, b = sqrt(a^2 - N) given (a^2 - N) is a perfect squareand we get p, q as a+b, a-b
```pythondef cracker(N, root):    bsquare = root**2 - N    if bsquare < 0:        return 0, 0    b = gmpy2.sqrt(root**2 - N)    if int(b)**2 != int(bsquare):        return 0, 0    p = int(root + b)     q = int(root - b)    return p, q
ans = getRoot(N)p, q = cracker(N, ans)```
This worked for every N=p*q I generated. but It failed on the N specified.The reason was that the condition root**2 > N was not met for any modulo square roots. The information of LSB was preserved in the roots but the information of MSB was lacking. So to fix this, I tried using bytes from the normal square root of N (which contains lots of MSBs) and bytes of one of the modulo square roots of N (which contains lots of LSBs), combining to give a 'pseudoRoot'. But using this rootapparently worked!
```pythondef getPartialRoot(N, root, nr_bytes = 32):    Nroot = int(gmpy2.sqrt(N)).to_bytes(128, 'big')    root = root.to_bytes(128, 'big')    newRoot = eval('0x' + Nroot[:nr_bytes].hex() + root[nr_bytes:].hex())    return newRoot
def solver(N, nr_bytes=32):    ans = getRoot(N)    if ans == 0:        # No proper roots were found, lets use a random root        possiblePartials = [int(i) for i in prime_mod_sqrt(gmpy2.mpz(N%2**2048), 2, 1024)]        for root in possiblePartials:            root = getPartialRoot(N, root, nr_bytes)            print("Using Partial Root: ", root)            p, q = cracker(N, root)            if p * q == N:                print("Root FOund!!!!", hex(p), hex(q))                return p, q    else:        p, q = cracker(N, ans)        if p * q == N:            print("Root FOund!!!!", hex(p), hex(q))            return p, q
N = 0xf7e6ddd1f49d9f875904d1280c675e0e03f4a02e2bec6ca62a2819d521441b727d313ec1b5f855e3f2a69516a3fea4e953435cbf7ac64062dd97157b6342912b7667b190fad36d54e378fede2a7a6d4cc801f1bc93159e405dd6d496bf6a11f04fdc04b842a84238cc3f677af67fa307b2b064a06d60f5a3d440c4cfffa4189e843602ba6f440a70668e071a9f18badffb11ed5cdfa6b49413cf4fa88b114f018eb0bba118f19dea2c08f4393b153bcbaf03ec86e2bab8f06e3c45acb6cd8d497062f5fdf19f73084a3a793fa20757178a546de902541dde7ff6f81de61a9e692145a793896a8726da7955dab9fc0668d3cfc55cd7a2d1d8b631f88cf5259ba1c = 0xf177639388156bd71b533d3934016cc76342efae0739cb317eb9235cdb97ae50b1aa097f16686d0e171dccc4ec2c3747f9fbaba4794ee057964734835400194fc2ffa68a5c6250d49abb68a9e540b3d8dc515682f1cd61f46352efc8cc4a1fe1d975c66b1d53f6b5ff25fbac9fa09ef7a3d7e93e2a53f9c1bc1db30eed92a30586388cfef4d68516a8fdebe5ebf9c7b483718437fcf8693acd3118544249b6e62d30afa7def37aecf4da999c1e2b686ca9caca1b84503b8794273381b51d06d0dfb9c19125ce30e67a8cf72321ca8c50a481e4b96bbbc5b8932e8d5a32fa040c3e29ced4c8bf3541e846f832a7f9406d263a592c0a9bce88be6aed043a9867a7
p, q = solver(N)```
We got the output:
    p = 0xfbeb1a7886ef44622066fa094ac36e67bae3ae428725ae40b33b4360f16d70ab1ab25e6ece7bdc51014e2e1a816cc3b16572d8d8aace175126f92981b765af06c6a719d16cdb83f4f9b1df22a37e6b5ddf4443a736d33190bd7f72b400829118b5bc520817f4210dbcefb930c1f5cce6c9fc9e3a903118b0a838cec76a090eaf    q = 0xfbeb1a7886ef44622066fa094ac36e67bae3ae428725ae40b33b4360f15502329379f55582e4fed5b5db1092155a566973adb60112301d087e794845d3fc0cce57e015acce21d2e86a8ee8cea59239dddf4443a736d33190bd7f72b400829118b5bc520817f4210dbcefb930c1f5cce6c9fc9e3a903118b0a838cec76a090eaf
And thus we decrypted the message as 
    Here is the message: CTF-BR{w3_n33d_more_resources_for_th3_0mni_pr0j3ct}\n