Skip to content

Latest commit

 

History

History

mprsa

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 

MPRSA (crypto)

ENG

PL

In the task we get RSA public key components, encrypted flag and source code used for encryption. The code contains a classic multiprime RSA implementation, with an interesting key generation code:

def key_gen(self, bits, prime_numbers=4):
	delta = randint(5, 15)
	bit_prime = int(bits // prime_numbers)

	P = [next_prime(number.getPrime(bit_prime) + 1)]
	for i in range(1, prime_numbers):
		P.append(next_prime(P[i - 1] * delta))

	n = self.__compute_module(P)
	phi = self.__compute_phi(P)

	for d_next in count(int(pow(P[0] // 2, 0.5)), -1):
		g, e, __ = gcdext(d_next, phi)
		if (1 < e < n) and (g == 1) and (gcd(phi, e) == 1):
			d = d_next
			break

	self.public_key = (e, n)
	self.secret_key = (d, n)

The initiall issue we noticed with this code, is that modulus n is composed of strongly non-random primes, since .next_prime is deterministic and delta multiplier is very small. We've seen some writeups for this task which attacked this vector.

Nevertheless, we have a bit of experience already with attacks on RSA and we've noticed one other interesting fact here:

for d_next in count(int(pow(P[0] // 2, 0.5)), -1):

Private key exponents starts from sqrt(p1/2) and we know that p1 is the smallest of the primes in n and they are of similar size, therefore p1 is smaller than n^(1/4).

And we know that if d < 1/3 n^(1/4) then we can use Wiener's attack to recover the private exponent. It's clear that we are lucky and we can recover the key with a bit of sage:

e = 2968282037100353640375137899109790499983904510372252123726372200136866453960017151334469454219618530252326391316368089337062513360207381202191915473462935477137523455963250056561696664667826520897145326882242932509636924316993816382503962649302107865422204292490659961123103322081852240437978613121365781016988448211321349469941008479597808471102164820173139919110860676464533506147455712945961147297193425603466185665772219928497258618754492859488589873906043003885893571962433509510568617898956135134801893952671289895841202079907023382879176353447845431980339763701845065932967492613174149948295178658632744337984598033199716909609691917091599333032421515584590767434316739374277008976624091929263313294017958203501962609986428734553144207841375915976037349385525685765751825435583700725710652618107250634373424713513298201017768173878869803169781015337283490319756398578109078482368725206020186761161884650413182297877151106135232838271785994275915310662858329477083914589917431343036266926436535406078268574331773960697696088892795445640924833807153106889785640164637689271399503064510417142492169690916011945805675154490404590528925067599406358567902459063109040410209462273031696409389388590120586013927889551821936657759836121166591
n = 7514486184413883943206134802309178399244378977612173666918494750761691891054947551148635071227769468578429057411933207521812645312852372491525360936618326543031520002708891330196401800722400435500157085990690437665009726219084442021182850506847121543952655588437818213790488615953323918596261471907835421407596459273791581399309405067626383928217548743866594178747621345881632069955681378662964970779524097614470204109881600043967504127490912520547758072473768719527077924134830122844355992675524808082077564650441063165395654489609498673176326527753016138066814814395200582603579511246113422000711435941608107654792503944786693356696589418688102700165482722623897706829970814110646089600275631212777003792683291735426294012686607809533096193939103941428766195023630255837719510277444701463006437791991196936648896229397094403915485049521731674097516242423233615004601202795680477677383876821794953563585797462940468885019612996080647173400509657498552114237186425176692867162493697752241051962151120715653607272964311445754089586884116532125369172407750688737448422035240971409748803419916890500367552066268915926436633178471526464741419410486387714614840372951024874043659727111073041432865136565615528171567027369016567760790667844170057

c_fracs = continued_fraction(e/n).convergents()
test_message = 42
test_message_encrypted = pow(test_message,e,n)
d = 0
for i in xrange(len(c_fracs)):
	if pow(test_message_encrypted,c_fracs[i].denom(),n) == test_message:
		d = c_fracs[i].denom()
		break
print(d)

And with d we can simply decode the flag:

from crypto_commons.generic import long_to_bytes


def main():
    n = 7514486184413883943206134802309178399244378977612173666918494750761691891054947551148635071227769468578429057411933207521812645312852372491525360936618326543031520002708891330196401800722400435500157085990690437665009726219084442021182850506847121543952655588437818213790488615953323918596261471907835421407596459273791581399309405067626383928217548743866594178747621345881632069955681378662964970779524097614470204109881600043967504127490912520547758072473768719527077924134830122844355992675524808082077564650441063165395654489609498673176326527753016138066814814395200582603579511246113422000711435941608107654792503944786693356696589418688102700165482722623897706829970814110646089600275631212777003792683291735426294012686607809533096193939103941428766195023630255837719510277444701463006437791991196936648896229397094403915485049521731674097516242423233615004601202795680477677383876821794953563585797462940468885019612996080647173400509657498552114237186425176692867162493697752241051962151120715653607272964311445754089586884116532125369172407750688737448422035240971409748803419916890500367552066268915926436633178471526464741419410486387714614840372951024874043659727111073041432865136565615528171567027369016567760790667844170057
    d = 9427062506559859200764441560060897853452091503537282553799991491531587159716894888858396729480853980609608783434755632459538177527336880678476984732352511
    ct = 4990981759460304744105598767593686181405870005282225829795794541021226151966053079510943795109726609634828370167775307839662644021918767556530119412853816585221569546843939870445288438295880322602517246037112564416212745954141726471664361647045729235670622890953655065235230427298013906810014221648290750692583336186843003229107021202513937560627163229698907224982160099413064560450430189221548918249561722797270239205285019947483419790983776163671611001827036804081081707549809205146146016914228431689911951835061650007130105435596899572248580145216361550470379538250892374083206633208114199207657470199269462010122511529769658733474277302308656490658251694852119519651331026206905848184310474442594518003923697214854504891077728222935182875777284193900483103844390422979429620136337089544700764854729601666550485708645758202313582038929079609869996469534041940940326632417337431671554125949585769777514656385405640728690453834779703498214246941789126527089991023766694976273980553865664242840580534044580685023115108182135139502041838131616984809782973256326815445038141870218251128685050551152554710812132312358766591390023888015234480632150114384947814031965110524912964541892010650475016456100706107619225121444952046171313017830946278
    print(long_to_bytes(pow(ct, d, n)))
main()

And we get:

Mr.D (12:10):
Okey, see you later ;)

Mr.D (19:30):
So can you help me?

Anonymous (19:31):
Yeah, we will have 10,000 falsified voters. Transfer 100000$ to my bank account: ctfzone{3177809746931830}

PL version

W zadaniu dostajemy klucz publiczny RSA, zaszyfrowaną flagę oraz kod szyfrowania wykorzystany do szyfrowania danych. Kod zawira klasyczną implementacje RSA opartego o wiele liczb pierwszych, z dość ciekawą logiką generacji klucza:

def key_gen(self, bits, prime_numbers=4):
	delta = randint(5, 15)
	bit_prime = int(bits // prime_numbers)

	P = [next_prime(number.getPrime(bit_prime) + 1)]
	for i in range(1, prime_numbers):
		P.append(next_prime(P[i - 1] * delta))

	n = self.__compute_module(P)
	phi = self.__compute_phi(P)

	for d_next in count(int(pow(P[0] // 2, 0.5)), -1):
		g, e, __ = gcdext(d_next, phi)
		if (1 < e < n) and (g == 1) and (gcd(phi, e) == 1):
			d = d_next
			break

	self.public_key = (e, n)
	self.secret_key = (d, n)

Pierwszą podatnością, którą zauważyliśmy w tym kodzie był sposób wyliczania modulusa n, który składa się z mocno nie-losowych liczb pierwszych, bo .next_prime jest deterministyczne a delta jest dość niewielka. Widzieliśmy writeupy które atakowały zadanie zgodnie z tym wektorem.

Niemniej mamy już trochę doświadczenia z atakami na RSA i zauważyliśmy inną ciekawostkę w kodzie:

for d_next in count(int(pow(P[0] // 2, 0.5)), -1):

Prywatny wykładnik szyfrujący zaczyna się od sqrt(p1/2) a wiemy że p1 jest najmniejszym czynnikiem pierwszym w n i że czynniki są zbliżonego rozmiaru, więc p1 musi być mniejsze od n^(1/4).

Wiemy też że jeśli d < 1/3 n^(1/4) to możemy użyć ataku Wienera aby odzyskać prywatny wykładnik szyfrujący. Jak widać mamy szczęście i możemy użyc prostego skryptu sage:

e = 2968282037100353640375137899109790499983904510372252123726372200136866453960017151334469454219618530252326391316368089337062513360207381202191915473462935477137523455963250056561696664667826520897145326882242932509636924316993816382503962649302107865422204292490659961123103322081852240437978613121365781016988448211321349469941008479597808471102164820173139919110860676464533506147455712945961147297193425603466185665772219928497258618754492859488589873906043003885893571962433509510568617898956135134801893952671289895841202079907023382879176353447845431980339763701845065932967492613174149948295178658632744337984598033199716909609691917091599333032421515584590767434316739374277008976624091929263313294017958203501962609986428734553144207841375915976037349385525685765751825435583700725710652618107250634373424713513298201017768173878869803169781015337283490319756398578109078482368725206020186761161884650413182297877151106135232838271785994275915310662858329477083914589917431343036266926436535406078268574331773960697696088892795445640924833807153106889785640164637689271399503064510417142492169690916011945805675154490404590528925067599406358567902459063109040410209462273031696409389388590120586013927889551821936657759836121166591
n = 7514486184413883943206134802309178399244378977612173666918494750761691891054947551148635071227769468578429057411933207521812645312852372491525360936618326543031520002708891330196401800722400435500157085990690437665009726219084442021182850506847121543952655588437818213790488615953323918596261471907835421407596459273791581399309405067626383928217548743866594178747621345881632069955681378662964970779524097614470204109881600043967504127490912520547758072473768719527077924134830122844355992675524808082077564650441063165395654489609498673176326527753016138066814814395200582603579511246113422000711435941608107654792503944786693356696589418688102700165482722623897706829970814110646089600275631212777003792683291735426294012686607809533096193939103941428766195023630255837719510277444701463006437791991196936648896229397094403915485049521731674097516242423233615004601202795680477677383876821794953563585797462940468885019612996080647173400509657498552114237186425176692867162493697752241051962151120715653607272964311445754089586884116532125369172407750688737448422035240971409748803419916890500367552066268915926436633178471526464741419410486387714614840372951024874043659727111073041432865136565615528171567027369016567760790667844170057

c_fracs = continued_fraction(e/n).convergents()
test_message = 42
test_message_encrypted = pow(test_message,e,n)
d = 0
for i in xrange(len(c_fracs)):
	if pow(test_message_encrypted,c_fracs[i].denom(),n) == test_message:
		d = c_fracs[i].denom()
		break
print(d)

I teraz mając już d możemy odszyfrować dane:

from crypto_commons.generic import long_to_bytes


def main():
    n = 7514486184413883943206134802309178399244378977612173666918494750761691891054947551148635071227769468578429057411933207521812645312852372491525360936618326543031520002708891330196401800722400435500157085990690437665009726219084442021182850506847121543952655588437818213790488615953323918596261471907835421407596459273791581399309405067626383928217548743866594178747621345881632069955681378662964970779524097614470204109881600043967504127490912520547758072473768719527077924134830122844355992675524808082077564650441063165395654489609498673176326527753016138066814814395200582603579511246113422000711435941608107654792503944786693356696589418688102700165482722623897706829970814110646089600275631212777003792683291735426294012686607809533096193939103941428766195023630255837719510277444701463006437791991196936648896229397094403915485049521731674097516242423233615004601202795680477677383876821794953563585797462940468885019612996080647173400509657498552114237186425176692867162493697752241051962151120715653607272964311445754089586884116532125369172407750688737448422035240971409748803419916890500367552066268915926436633178471526464741419410486387714614840372951024874043659727111073041432865136565615528171567027369016567760790667844170057
    d = 9427062506559859200764441560060897853452091503537282553799991491531587159716894888858396729480853980609608783434755632459538177527336880678476984732352511
    ct = 4990981759460304744105598767593686181405870005282225829795794541021226151966053079510943795109726609634828370167775307839662644021918767556530119412853816585221569546843939870445288438295880322602517246037112564416212745954141726471664361647045729235670622890953655065235230427298013906810014221648290750692583336186843003229107021202513937560627163229698907224982160099413064560450430189221548918249561722797270239205285019947483419790983776163671611001827036804081081707549809205146146016914228431689911951835061650007130105435596899572248580145216361550470379538250892374083206633208114199207657470199269462010122511529769658733474277302308656490658251694852119519651331026206905848184310474442594518003923697214854504891077728222935182875777284193900483103844390422979429620136337089544700764854729601666550485708645758202313582038929079609869996469534041940940326632417337431671554125949585769777514656385405640728690453834779703498214246941789126527089991023766694976273980553865664242840580534044580685023115108182135139502041838131616984809782973256326815445038141870218251128685050551152554710812132312358766591390023888015234480632150114384947814031965110524912964541892010650475016456100706107619225121444952046171313017830946278
    print(long_to_bytes(pow(ct, d, n)))
main()

I dostajemy:

Mr.D (12:10):
Okey, see you later ;)

Mr.D (19:30):
So can you help me?

Anonymous (19:31):
Yeah, we will have 10,000 falsified voters. Transfer 100000$ to my bank account: ctfzone{3177809746931830}