# Hack.lu CTF 2023
> Vì thời gian diễn ra giải này và giải Duy Tân diễn ra hầu hết là trùng nhau nên sau khi ~~bị đề Duy Tân giã tan nát~~ giải Duy Tân kết thúc thì team mình chỉ có vài tiếng còn lại để làm. Tuy nhiên team mình cũng kịp làm được 2 câu và mình đóng góp được 1 câu crypto ~~siêu dễ~~ 😁. Sau đây là chi tiết write-up của mình cho câu đó.
## Lucky number

- Source:
```python=
#!/usr/bin/env python
#hacklu23 Baby Crypyo Challenge
import math
import random
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
import base64
import os
def add(e): return e+(length-len(e)%length)*chr(length-len(e)%length)
def remove(e): return e[0:-ord(e[-1:])]
length=16
def main():
flag= os.environ["FLAG"]
print("Starting Challenge")
key=get_random_bytes(32)
message=add(flag)
iv = get_random_bytes(length)
cipher = AES.new(key,AES.MODE_CBC,iv)
cipher_bytes = base64.b64encode(iv+cipher.encrypt(message.encode("utf8")))
print(cipher_bytes.decode())
for l in range(0,5):
A=[]
print("You know the moment when you have this special number that gives you luck? Great cause I forgot mine")
data2=input()
print("I also had a second lucky number, but for some reason I don't remember it either :(")
data3=input()
v=data2.strip()
w=data3.strip()
if not v.isnumeric() or not w.isnumeric():
print("You sure both of these are numbers?")
continue
s=int(data2)
t=int(data3)
if s<random.randrange(10000,20000):
print("I have the feeling the first number might be too small")
continue
if s>random.randrange(150000000000,200000000000):
print("I have the feeling the first number might be too big")
continue
if t>42:
print("I have the feeling the second number might be too big")
continue
n=2**t-1
sent=False
for i in range(2,int(n**0.5)+1):
if (n%i) == 0:
print("The second number didn't bring me any luck...")
sent = True
break
if sent:
continue
u=t-1
number=(2**u)*(2**(t)-1)
sqrt_num=math.isqrt(s)
for i in range(1,sqrt_num+1):
if s%i==0:
A.append(i)
if i != s//i and s//i != s:
A.append(s//i)
total=sum(A)
if total==s==number:
decoded=base64.b64decode(cipher_bytes)
cipher=AES.new(key,AES.MODE_CBC,iv)
decoded_bytes=remove(cipher.decrypt(decoded[length:]))
print("You found them, well done! Here have something for your efforts: ")
print(decoded_bytes.decode())
exit()
else:
print("Hm sadge, those don't seem to be my lucky numbers...😞")
print("Math is such a cool concept, let's see if you can use it a little more...")
exit()
if __name__ == "__main__":
main()
```
- Source bài này khá lan man, đơn giải bài này chỉ yêu cầu chúng ta nhập 2 số s và thỏa mãn các điều kiện sau:
- $t<=42$ (1)
- `20000 < s = (2**(t-1))*(2**(t)-1) < 150000000000`(2)
- `2**t-1` là số nguyên tố (số nguyên tố dạng này còn có tên gọi là [mersenne prime](https://vi.wikipedia.org/wiki/S%E1%BB%91_nguy%C3%AAn_t%E1%BB%91_Mersenne)(3)
- $s = 2^{t-1}.(2^t -1)$ là số hoàn hảo (4)
- Nghe điều kiện (4) có vẻ khá khoai 😥, tuy nhiên khi mình đọc về số hoàn hảo trên wiki thì mình phát hiện:

- Ồ, vậy ngon rồi :D, vậy ta chỉ cần tìm số t thỏa 3 điều kiện (1),(2),(3) (do t <= 42 nên chỉ cần brute-force), sau đó tính $s = 2^{t-1}.(2^t -1)$ rồi gởi lên server thôi :v
- Solve.py
```python=
from pwn import *
import base64
from Crypto.Util.number import isPrime
r = remote("flu.xxx", 10010)
for i in range(42):
if isPrime(2**i - 1):
t = i
s = (2**(t-1))*(2**(t)-1)
if 20000 < s < 150000000000:
break
r.sendlineafter(b'You know the moment when you have this special number that gives you luck? Great cause I forgot mine\n', str(s).encode())
r.sendlineafter(b"I also had a second lucky number, but for some reason I don't remember it either :(\n", str(t).encode())
r.interactive()
```

> Flag: flag{luck_0n_fr1d4y_th3_13th?}