Tags: coppersmith crypto re
Rating: 2.0
I was told by a cooopersmith that I should send hackers encrypted messages because it is secure.
coooppersmith.challenges.ooo 5000
Reversing is done. There are two steps to get the flag.
test()
: Guess two consecutive crandom output which seed is time(NULL)
.getEncFlag()
: Obtain encrypted flag and decrypt it.Public modulus n = p * q
is generated by weird logic. It first gets 60 byte input seed
from user, and generate 512 bit prime prime
. Below is the genPrime()
logic.
def genPrime(seed):
assert len(seed) <= 120
v = int(seed, 16)
l = len(seed)
shift_val = 4 * (128 - l)
v8 = v << shift_val
v7 = 2 ** (shift_val / 2)
v2 = 0
for _ in range(100):
r = randrange(v7)
prime = r + v8
while prime >> shift_val == v:
if isPrime(prime):
v2 = 1
break
prime += 1
if v2:
break
return prime
After that, service uses prime
as seed to generate two 1024 bit primes p
, q
using function genRSAPublicKey()
. It iterates until it generates primes with following contraints, where x0
, y0
are 512 bit values.
p = 2 * prime * x0 + 1
q = 2 * prime * y0 + 1
n = (2 * prime * x0 + 1) * (2 * prime * y0 + 1)
First send seed
as value of 'ff' * 60
The entropy of prime
is controlled by seed
. If I use maximum value of seed
, It becomes feasible to brute and find out the value of prime
. When seed
has 60 byte length, random r
for genPrime()
falls in range(65536)
and can be easily guessed. Below is the example output of genPrime()
in hex.
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000a0df
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00009f61
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000005cb
Public modulus n
satifies (n - 1) % prime == 0
because of upper prime gen logic. By using this fact, brute and obtain prime
.
start = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000
while True:
if (n - 1) % start == 0:
print(start)
exit()
start += 1
Secondly, guess two 32bit crand values. This is obvious because value of seed is time(NULL)
. Use ctypes to call srand()
.
class RandomWrapper():
def __init__(self, delta, seed=None):
self.c = ctypes.CDLL('/lib/x86_64-linux-gnu/libc.so.6')
if seed:
self.seed = seed - delta
else:
self.seed = self.c.time(0) - delta
pwn.log.info('seed: {}'.format(self.seed))
self.c.srand(self.seed)
self.Random = self.c.rand
def Random(self):
return self.Random()
delta = 0
r = RandomWrapper(delta)
s = r.Random()
t = r.Random()
ans = (s + t) & 0xffffffff
Now we have c
and prime
. To factor n = (2 * prime * x0 + 1) * (2 * prime * y0 + 1)
, I have to find out x0
and y0
. Bivariate coppersmith may solve the equation.
By using this great implementation of Coron's reformulation of Coppersmith's algorithm for finding small integer roots of bivariate polynomial over n
, I recovered x0
and y0
. Below is the polynomial constructed for applicating coppersmith.
X = Y = 2 ** 512 # Estimated size of x0, y0
P.<x, y> = PolynomialRing(ZZ)
pol = (2 * p0 * x + 1) * (2 * p0 * y + 1) - n
x0, y0 = coron(pol, X, Y, k=2, debug=False)[0]
assert n == (2 * p0 * x0 + 1) * (2 * p0 * y0 + 1)
p = 2 * p0 * x0 + 1
q = 2 * p0 * y0 + 1
assert p * q == n
Factors known, get the flag!
OOO{Be_A_Flexible_Coppersmith}
Original binary: service
Exploit code: solve.py, coppersmith.sage