# Hero 3
Crypto Challenge using Fermat's Little Theorem (YBN CTF 2024)
### Challenge Details
> Title: Hero 3 \
> Description: Because you were so good, I made it even harder! I even asked Fermat to help. I wonder what he did hmm.... \
> Attachments: `https://ctf.ybn.sg/files/b782a8f915f3ca4b5371263e8cc33fbe/Hero_3_chall.py` \
> Instance: `nc tcp.ybn.sg 20259`
### TLDR
```py
from Crypto.Util.number import long_to_bytes
e = 3494680627
p2 = 7700290168101389106225891477639129929001226792416017984661341954505371362577871619513480110997045873606558858973494882905175759502920368168780896854509931
ct = 896937076301392762743958156834404549614745613199699888403224725624929617011876252397149275690138390286900204448543394686055815777377732974510626768032590
d = pow(e, -1, p2 - 1)
f = pow(ct, d, p2)
print(long_to_bytes(f))
```
### Writeup
In the given file, we have:
```py
from Crypto.Util.number import getPrime, bytes_to_long, long_to_bytes
import random
flag = b"YBN24{????????????????????????????????????????}"
try:
p1 = getPrime(64)
p2 = getPrime(512)
e = getPrime(32)
c = str(random.randint(0,999999999))
a = input("a: ")
b = input("b: ")
check = ' 01qwertyuiopasdfghjklzxcvbnm%^&*()+-/><'
for char in a:
assert char not in check and char.isascii()
for char in b:
assert char not in check and char.isascii()
a = eval(a + c)
b = eval(b)
d = pow(a, b, getPrime(128))
z = pow(p1, p2-d, p2)
ct = pow(bytes_to_long(flag) * z, e, p2)
print("e =", e)
print("p2 =", p2)
print("ct =", ct)
except Exception as e:
print(e)
quit()
```
We also have the hint that this is using Fermat's Little Theorem, which states that:
> **If $p$ is a prime number, then for any integer $a$**,
> $$a^p \equiv a \pmod p$$
> **If $a$ and $p$ are coprime, then equivalently**,
> $$a^{p - 1} \equiv 1 \pmod p$$
Identify that in line 26:
```py
z = pow(p1, p2-d, p2)
```
If $d = 1$, then, since $p_1$ and $p_2$ are coprime, we can use Fermat's Little Theorem to derive that $z = 1$ \
To make $d = 1$, we can craft an input that would make $b = 0$, so that:
```py
d = pow(a, b, getPrime(128))
= pow(a, 0, getPrime(128))
= 1
```
Crafting an input that would make $b = 0$ is easy, as they use an unsafe eval. One could use `[]=={}` for example.
Finally, we solve for `bytes_to_long(flag)` in \
$\text{ct} = \text{bytes\_{}to\_{}long(flag)}^e \pmod 2 $
with
```py
d = pow(e, -1, p2 - 1)
f = pow(ct, d, p2)
print(long_to_bytes(f))
```
Connecting to the challenge instance, and inputting `a: 3` (arbitrary number) and `b: []=={}` (evaluates to 0), I got:
```py
e = 3494680627
p2 = 7700290168101389106225891477639129929001226792416017984661341954505371362577871619513480110997045873606558858973494882905175759502920368168780896854509931
ct = 896937076301392762743958156834404549614745613199699888403224725624929617011876252397149275690138390286900204448543394686055815777377732974510626768032590
```
Paste that into the solve script:
```py
from Crypto.Util.number import long_to_bytes
e = 3494680627
p2 = 7700290168101389106225891477639129929001226792416017984661341954505371362577871619513480110997045873606558858973494882905175759502920368168780896854509931
ct = 896937076301392762743958156834404549614745613199699888403224725624929617011876252397149275690138390286900204448543394686055815777377732974510626768032590
d = pow(e, -1, p2 - 1)
f = pow(ct, d, p2)
print(long_to_bytes(f))
```
And we get:
:::spoiler Flag
YBN24{r0und1ng_up_w1th_f3rm4t's_l1ttl3_th30r3m}
:::
\
\
\
Main Writeups Page: https://hackmd.io/@ctf-lol/ybnctf2024