Hack.lu-Qualifier-2023/Crypto/Lucky Numbers Writeup
tags: ctf,crypto,beginner-friendly
Hack.lu-Qualifier-2023/Crypto/Lucky Numbers Writeup
1. challenge
- Author: zer0
- beginner-friendly!
Help me to find my lucky numbers. Bad luck is around…
nc flu.xxx 10010
lucky_number.py
#!/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()
2. 分析
一个简单数学问题,也可以忽略数学关系,直接爆破。总之是一个新手友好题。
10000 <= s <= 200000000000, 0 <= t <= 42
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 素数
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
$s = 2^{t-1}*(2^t-1)$
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:
# print flag
根据 $s = 2^{t-1}*(2^t-1)$ 直接生成 t 所对应的 s 即可,总计至多 42 个组合,不必处理 A 的生成。
实际上, s=sum(A) 是直接成立的。
$$ \begin{align*} s &= sum(A) \newline &= 2^0 + (2^1 + 2^{t-2}*(2^t-1)) + … + (2^i + 2^{t-i-1}*(2^t-1)) + … + (2^{t-1} + 2^0*(2^t-1)) \newline &= (2^0 + 2^1 + … + 2^i + … + 2^{t-1}) + 2^0*(2^t-1) + … + 2^{t-2}*(2^t-1) \newline &= 2^t - 1 + (2^{t-1}-1)*(2^t-1) \newline &= 2^{t-1}*(2^t-1) \end{align*} $$
3. solve
from Crypto.Util.number import isPrime
def solve():
answer = []
for t in range(42):
n = 2**t-1
if isPrime(n):
u = t-1
s = (2**u)*(2**(t)-1)
if 10000 <= s <= 200000000000:
answer.append((s, t))
return answer
answer = solve(); answer
[(33550336, 13), (8589869056, 17), (137438691328, 19)]
from pwn import *
r = remote(b"flu.xxx", 10010)
def io(s, t):
r.sendlineafter(b"Great cause I forgot mine", str(s).encode())
r.sendlineafter(b"I don't remember it either :(", str(t).encode())
r.recvuntil(b"for your efforts: \n")
flag = r.recv()
print(flag)
io(*answer[0])
r.close()
[x] Opening connection to b'flu.xxx' on port 10010
[x] Opening connection to b'flu.xxx' on port 10010: Trying 31.22.123.45
[+] Opening connection to b'flu.xxx' on port 10010: Done
b'flag{luck_0n_fr1d4y_th3_13th?}\n'
[*] Closed connection to b'flu.xxx' port 10010
4. Something interesting
官方服务器似乎少了一行代码:
if s<random.randrange(10000,20000):
print("I have the feeling the first number might be too small")
continue
导致有一些非预期解
❯ nc flu.xxx 10010
Starting Challenge
S8mQF5KqwDOu1PioMqy+ogh7o6V4YFEwy3ZNOLFFyBRPV4A8nsBoKbM1K8/1cnLu
You know the moment when you have this special number that gives you luck? Great cause I forgot mine
1
I also had a second lucky number, but for some reason I don't remember it either :(
1
You found them, well done! Here have something for your efforts:
flag{luck_0n_fr1d4y_th3_13th?}
本文同步发表于
- blog: ssst0n3.github.io
- 公众号: 石头的安全料理屋
- 知乎专栏: 石头的安全料理屋