|
| 1 | +# MPRSA (crypto) |
| 2 | + |
| 3 | +## ENG |
| 4 | +[PL](#pl-version) |
| 5 | + |
| 6 | +In the task we get [RSA public key components](public.txt), [encrypted flag](data.enc) and [source code](mprsa.py) used for encryption. |
| 7 | +The code contains a classic multiprime RSA implementation, with an interesting key generation code: |
| 8 | + |
| 9 | +```python |
| 10 | +def key_gen(self, bits, prime_numbers=4): |
| 11 | + delta = randint(5, 15) |
| 12 | + bit_prime = int(bits // prime_numbers) |
| 13 | + |
| 14 | + P = [next_prime(number.getPrime(bit_prime) + 1)] |
| 15 | + for i in range(1, prime_numbers): |
| 16 | + P.append(next_prime(P[i - 1] * delta)) |
| 17 | + |
| 18 | + n = self.__compute_module(P) |
| 19 | + phi = self.__compute_phi(P) |
| 20 | + |
| 21 | + for d_next in count(int(pow(P[0] // 2, 0.5)), -1): |
| 22 | + g, e, __ = gcdext(d_next, phi) |
| 23 | + if (1 < e < n) and (g == 1) and (gcd(phi, e) == 1): |
| 24 | + d = d_next |
| 25 | + break |
| 26 | + |
| 27 | + self.public_key = (e, n) |
| 28 | + self.secret_key = (d, n) |
| 29 | +``` |
| 30 | + |
| 31 | +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. |
| 32 | +We've seen some writeups for this task which attacked this vector. |
| 33 | + |
| 34 | +Nevertheless, we have a bit of experience already with attacks on RSA and we've noticed one other interesting fact here: |
| 35 | + |
| 36 | +```python |
| 37 | +for d_next in count(int(pow(P[0] // 2, 0.5)), -1): |
| 38 | +``` |
| 39 | + |
| 40 | +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)`. |
| 41 | + |
| 42 | +And we know that if `d < 1/3 n^(1/4)` then we can use Wiener's attack to recover the private exponent. |
| 43 | +It's clear that we are lucky and we can recover the key with a bit of sage: |
| 44 | + |
| 45 | +```python |
| 46 | +e = 2968282037100353640375137899109790499983904510372252123726372200136866453960017151334469454219618530252326391316368089337062513360207381202191915473462935477137523455963250056561696664667826520897145326882242932509636924316993816382503962649302107865422204292490659961123103322081852240437978613121365781016988448211321349469941008479597808471102164820173139919110860676464533506147455712945961147297193425603466185665772219928497258618754492859488589873906043003885893571962433509510568617898956135134801893952671289895841202079907023382879176353447845431980339763701845065932967492613174149948295178658632744337984598033199716909609691917091599333032421515584590767434316739374277008976624091929263313294017958203501962609986428734553144207841375915976037349385525685765751825435583700725710652618107250634373424713513298201017768173878869803169781015337283490319756398578109078482368725206020186761161884650413182297877151106135232838271785994275915310662858329477083914589917431343036266926436535406078268574331773960697696088892795445640924833807153106889785640164637689271399503064510417142492169690916011945805675154490404590528925067599406358567902459063109040410209462273031696409389388590120586013927889551821936657759836121166591 |
| 47 | +n = 7514486184413883943206134802309178399244378977612173666918494750761691891054947551148635071227769468578429057411933207521812645312852372491525360936618326543031520002708891330196401800722400435500157085990690437665009726219084442021182850506847121543952655588437818213790488615953323918596261471907835421407596459273791581399309405067626383928217548743866594178747621345881632069955681378662964970779524097614470204109881600043967504127490912520547758072473768719527077924134830122844355992675524808082077564650441063165395654489609498673176326527753016138066814814395200582603579511246113422000711435941608107654792503944786693356696589418688102700165482722623897706829970814110646089600275631212777003792683291735426294012686607809533096193939103941428766195023630255837719510277444701463006437791991196936648896229397094403915485049521731674097516242423233615004601202795680477677383876821794953563585797462940468885019612996080647173400509657498552114237186425176692867162493697752241051962151120715653607272964311445754089586884116532125369172407750688737448422035240971409748803419916890500367552066268915926436633178471526464741419410486387714614840372951024874043659727111073041432865136565615528171567027369016567760790667844170057 |
| 48 | + |
| 49 | +c_fracs = continued_fraction(e/n).convergents() |
| 50 | +test_message = 42 |
| 51 | +test_message_encrypted = pow(test_message,e,n) |
| 52 | +d = 0 |
| 53 | +for i in xrange(len(c_fracs)): |
| 54 | + if pow(test_message_encrypted,c_fracs[i].denom(),n) == test_message: |
| 55 | + d = c_fracs[i].denom() |
| 56 | + break |
| 57 | +print(d) |
| 58 | +``` |
| 59 | + |
| 60 | +And with `d` we can simply decode the flag: |
| 61 | + |
| 62 | +```python |
| 63 | +from crypto_commons.generic import long_to_bytes |
| 64 | + |
| 65 | + |
| 66 | +def main(): |
| 67 | + n = 7514486184413883943206134802309178399244378977612173666918494750761691891054947551148635071227769468578429057411933207521812645312852372491525360936618326543031520002708891330196401800722400435500157085990690437665009726219084442021182850506847121543952655588437818213790488615953323918596261471907835421407596459273791581399309405067626383928217548743866594178747621345881632069955681378662964970779524097614470204109881600043967504127490912520547758072473768719527077924134830122844355992675524808082077564650441063165395654489609498673176326527753016138066814814395200582603579511246113422000711435941608107654792503944786693356696589418688102700165482722623897706829970814110646089600275631212777003792683291735426294012686607809533096193939103941428766195023630255837719510277444701463006437791991196936648896229397094403915485049521731674097516242423233615004601202795680477677383876821794953563585797462940468885019612996080647173400509657498552114237186425176692867162493697752241051962151120715653607272964311445754089586884116532125369172407750688737448422035240971409748803419916890500367552066268915926436633178471526464741419410486387714614840372951024874043659727111073041432865136565615528171567027369016567760790667844170057 |
| 68 | + d = 9427062506559859200764441560060897853452091503537282553799991491531587159716894888858396729480853980609608783434755632459538177527336880678476984732352511 |
| 69 | + ct = 4990981759460304744105598767593686181405870005282225829795794541021226151966053079510943795109726609634828370167775307839662644021918767556530119412853816585221569546843939870445288438295880322602517246037112564416212745954141726471664361647045729235670622890953655065235230427298013906810014221648290750692583336186843003229107021202513937560627163229698907224982160099413064560450430189221548918249561722797270239205285019947483419790983776163671611001827036804081081707549809205146146016914228431689911951835061650007130105435596899572248580145216361550470379538250892374083206633208114199207657470199269462010122511529769658733474277302308656490658251694852119519651331026206905848184310474442594518003923697214854504891077728222935182875777284193900483103844390422979429620136337089544700764854729601666550485708645758202313582038929079609869996469534041940940326632417337431671554125949585769777514656385405640728690453834779703498214246941789126527089991023766694976273980553865664242840580534044580685023115108182135139502041838131616984809782973256326815445038141870218251128685050551152554710812132312358766591390023888015234480632150114384947814031965110524912964541892010650475016456100706107619225121444952046171313017830946278 |
| 70 | + print(long_to_bytes(pow(ct, d, n))) |
| 71 | +main() |
| 72 | +``` |
| 73 | + |
| 74 | +And we get: |
| 75 | + |
| 76 | +``` |
| 77 | +Mr.D (12:10): |
| 78 | +Okey, see you later ;) |
| 79 | +
|
| 80 | +Mr.D (19:30): |
| 81 | +So can you help me? |
| 82 | +
|
| 83 | +Anonymous (19:31): |
| 84 | +Yeah, we will have 10,000 falsified voters. Transfer 100000$ to my bank account: ctfzone{3177809746931830} |
| 85 | +``` |
| 86 | + |
| 87 | +## PL version |
| 88 | + |
| 89 | +W zadaniu dostajemy [klucz publiczny RSA](public.txt), [zaszyfrowaną flagę](data.enc) oraz [kod szyfrowania](mprsa.py) wykorzystany do szyfrowania danych. |
| 90 | +Kod zawira klasyczną implementacje RSA opartego o wiele liczb pierwszych, z dość ciekawą logiką generacji klucza: |
| 91 | + |
| 92 | +```python |
| 93 | +def key_gen(self, bits, prime_numbers=4): |
| 94 | + delta = randint(5, 15) |
| 95 | + bit_prime = int(bits // prime_numbers) |
| 96 | + |
| 97 | + P = [next_prime(number.getPrime(bit_prime) + 1)] |
| 98 | + for i in range(1, prime_numbers): |
| 99 | + P.append(next_prime(P[i - 1] * delta)) |
| 100 | + |
| 101 | + n = self.__compute_module(P) |
| 102 | + phi = self.__compute_phi(P) |
| 103 | + |
| 104 | + for d_next in count(int(pow(P[0] // 2, 0.5)), -1): |
| 105 | + g, e, __ = gcdext(d_next, phi) |
| 106 | + if (1 < e < n) and (g == 1) and (gcd(phi, e) == 1): |
| 107 | + d = d_next |
| 108 | + break |
| 109 | + |
| 110 | + self.public_key = (e, n) |
| 111 | + self.secret_key = (d, n) |
| 112 | +``` |
| 113 | + |
| 114 | +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. |
| 115 | +Widzieliśmy writeupy które atakowały zadanie zgodnie z tym wektorem. |
| 116 | + |
| 117 | +Niemniej mamy już trochę doświadczenia z atakami na RSA i zauważyliśmy inną ciekawostkę w kodzie: |
| 118 | + |
| 119 | +```python |
| 120 | +for d_next in count(int(pow(P[0] // 2, 0.5)), -1): |
| 121 | +``` |
| 122 | + |
| 123 | +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)`. |
| 124 | + |
| 125 | +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. |
| 126 | +Jak widać mamy szczęście i możemy użyc prostego skryptu sage: |
| 127 | + |
| 128 | +```python |
| 129 | +e = 2968282037100353640375137899109790499983904510372252123726372200136866453960017151334469454219618530252326391316368089337062513360207381202191915473462935477137523455963250056561696664667826520897145326882242932509636924316993816382503962649302107865422204292490659961123103322081852240437978613121365781016988448211321349469941008479597808471102164820173139919110860676464533506147455712945961147297193425603466185665772219928497258618754492859488589873906043003885893571962433509510568617898956135134801893952671289895841202079907023382879176353447845431980339763701845065932967492613174149948295178658632744337984598033199716909609691917091599333032421515584590767434316739374277008976624091929263313294017958203501962609986428734553144207841375915976037349385525685765751825435583700725710652618107250634373424713513298201017768173878869803169781015337283490319756398578109078482368725206020186761161884650413182297877151106135232838271785994275915310662858329477083914589917431343036266926436535406078268574331773960697696088892795445640924833807153106889785640164637689271399503064510417142492169690916011945805675154490404590528925067599406358567902459063109040410209462273031696409389388590120586013927889551821936657759836121166591 |
| 130 | +n = 7514486184413883943206134802309178399244378977612173666918494750761691891054947551148635071227769468578429057411933207521812645312852372491525360936618326543031520002708891330196401800722400435500157085990690437665009726219084442021182850506847121543952655588437818213790488615953323918596261471907835421407596459273791581399309405067626383928217548743866594178747621345881632069955681378662964970779524097614470204109881600043967504127490912520547758072473768719527077924134830122844355992675524808082077564650441063165395654489609498673176326527753016138066814814395200582603579511246113422000711435941608107654792503944786693356696589418688102700165482722623897706829970814110646089600275631212777003792683291735426294012686607809533096193939103941428766195023630255837719510277444701463006437791991196936648896229397094403915485049521731674097516242423233615004601202795680477677383876821794953563585797462940468885019612996080647173400509657498552114237186425176692867162493697752241051962151120715653607272964311445754089586884116532125369172407750688737448422035240971409748803419916890500367552066268915926436633178471526464741419410486387714614840372951024874043659727111073041432865136565615528171567027369016567760790667844170057 |
| 131 | + |
| 132 | +c_fracs = continued_fraction(e/n).convergents() |
| 133 | +test_message = 42 |
| 134 | +test_message_encrypted = pow(test_message,e,n) |
| 135 | +d = 0 |
| 136 | +for i in xrange(len(c_fracs)): |
| 137 | + if pow(test_message_encrypted,c_fracs[i].denom(),n) == test_message: |
| 138 | + d = c_fracs[i].denom() |
| 139 | + break |
| 140 | +print(d) |
| 141 | +``` |
| 142 | + |
| 143 | +I teraz mając już `d` możemy odszyfrować dane: |
| 144 | + |
| 145 | +```python |
| 146 | +from crypto_commons.generic import long_to_bytes |
| 147 | + |
| 148 | + |
| 149 | +def main(): |
| 150 | + n = 7514486184413883943206134802309178399244378977612173666918494750761691891054947551148635071227769468578429057411933207521812645312852372491525360936618326543031520002708891330196401800722400435500157085990690437665009726219084442021182850506847121543952655588437818213790488615953323918596261471907835421407596459273791581399309405067626383928217548743866594178747621345881632069955681378662964970779524097614470204109881600043967504127490912520547758072473768719527077924134830122844355992675524808082077564650441063165395654489609498673176326527753016138066814814395200582603579511246113422000711435941608107654792503944786693356696589418688102700165482722623897706829970814110646089600275631212777003792683291735426294012686607809533096193939103941428766195023630255837719510277444701463006437791991196936648896229397094403915485049521731674097516242423233615004601202795680477677383876821794953563585797462940468885019612996080647173400509657498552114237186425176692867162493697752241051962151120715653607272964311445754089586884116532125369172407750688737448422035240971409748803419916890500367552066268915926436633178471526464741419410486387714614840372951024874043659727111073041432865136565615528171567027369016567760790667844170057 |
| 151 | + d = 9427062506559859200764441560060897853452091503537282553799991491531587159716894888858396729480853980609608783434755632459538177527336880678476984732352511 |
| 152 | + ct = 4990981759460304744105598767593686181405870005282225829795794541021226151966053079510943795109726609634828370167775307839662644021918767556530119412853816585221569546843939870445288438295880322602517246037112564416212745954141726471664361647045729235670622890953655065235230427298013906810014221648290750692583336186843003229107021202513937560627163229698907224982160099413064560450430189221548918249561722797270239205285019947483419790983776163671611001827036804081081707549809205146146016914228431689911951835061650007130105435596899572248580145216361550470379538250892374083206633208114199207657470199269462010122511529769658733474277302308656490658251694852119519651331026206905848184310474442594518003923697214854504891077728222935182875777284193900483103844390422979429620136337089544700764854729601666550485708645758202313582038929079609869996469534041940940326632417337431671554125949585769777514656385405640728690453834779703498214246941789126527089991023766694976273980553865664242840580534044580685023115108182135139502041838131616984809782973256326815445038141870218251128685050551152554710812132312358766591390023888015234480632150114384947814031965110524912964541892010650475016456100706107619225121444952046171313017830946278 |
| 153 | + print(long_to_bytes(pow(ct, d, n))) |
| 154 | +main() |
| 155 | +``` |
| 156 | + |
| 157 | +I dostajemy: |
| 158 | + |
| 159 | +``` |
| 160 | +Mr.D (12:10): |
| 161 | +Okey, see you later ;) |
| 162 | +
|
| 163 | +Mr.D (19:30): |
| 164 | +So can you help me? |
| 165 | +
|
| 166 | +Anonymous (19:31): |
| 167 | +Yeah, we will have 10,000 falsified voters. Transfer 100000$ to my bank account: ctfzone{3177809746931830} |
| 168 | +``` |
0 commit comments