But to satisfy those skillful player, I still generated some “tougher” challenges … here are there writeups~ I done the writeup for those harder challenges since those easy ones could be answered on our DC channel/GPTable/Google…… Hope you enjoy them~
(This is also what I want those newbies who were trying to solve this challenge can learn from the CTF!)
Anyway, for those who would have headache after seeing the definition on wikipedia, I would explain it as easy as possible here: For a matrix like this:
1 2 3
[[a1, b1, c1] [a2, b2, c2] [a3, b3, c3]]
You can find some short(may be the shortest) linear combination originated from the three vectors up there. ahh… Linear Combination is something like this:
from Crypto.Util.number import * from Secret import flag2
defleak_info(primes): mod=getPrime(4096) parameters=[getPrime(4096) for _ inrange(3)] cnt=0 for i inrange(3): cnt+=primes[i]*parameters[i] cnt%=mod print(f"---leak info---\n{cnt=}\n{parameters=}\n{mod=}")
primes=[getPrime(512) for _ inrange(3)] p, q, r=primes[0], primes[1], primes[2] n=p*q*r e=0x10001 c=pow(bytes_to_long(flag2), e, n) print(f"{n=}\n{e=}\n{c=}") leak_info(primes)
The main part for the challenge is to recover the primes value through the given Diophantine(indefinite) equation, which can done by constructing this matrix:
and calculate its LLL reduction. Why? This is your homework :P Using sagemath to done the solve script would be the easiest way since it has a LLL inate!
A=[[0]*5 for _ in range(5)] for i in range(3): A[i][0]=parameters[i] A[i][i+1]=1
A[3][0]=cnt A[3][4]=pow(2, 2000) A[4][0]=mod
A = Matrix(ZZ,A) r = A.LLL()
for arr in r: if arr[0]==0: p, q, r=int(-arr[1]), int(-arr[2]), int(-arr[3]) phi=(p-1)*(q-1)*(r-1) d=inverse(e, phi) print(long_to_bytes(pow(int(c), int(d), int(n))))
Lion Prime
This challenge had been solved by a VERY EASY unintended method so I made a revenge one XD. Source Code:
x=np.array(datas[:60]).prod() if (pow(5, x)-pow(3, x))%(pow(2, x)+115)==0: print('NHNC{FAKE_FLAG}')
else: print('The cutest lion doesn\'t like your logs...', x)
if __name__=='__main__': print(banner) logs=[] oops=random.randint(1, 2**64) print(f"Oops: {oops}") whileTrue: p=int(input("Input your number to check if it's a Lion Prime: ")) if p==0: break if lion_prime(p): print("This is a Lion Prime, prove me wrong !!!") cur=[] for i inrange(1, 4): cur.append(int(input(f"Factor {i}: "))) if cur[i-1]==1: print("Invalid Factor!!!") exit()
if cur[0]*cur[1]*cur[2]==p: print("OK, I'll report the issue") logs.append(p^oops) else: print("WA / 0") else: print("This is not a Lion Prime QwQ...") iflen(logs)!=len(set(logs)): print("Collision!!!") exit() checker(logs)
The first part of the challenge is to deceive my lion_prime function (which is doing Miller-Rabin primality test) by giving three factors(f1, f2, f3 and f1*f2*f3==p), though I blocked the use of 1, I didn’t block -1…(Thanks a lot for telling me @eggroll(OAAAO))
After that, is easy to see that if the logs array is only [0], it would pass the checker function 100% So the exploitation was just like… Keep trying until the oops value is a prime-> Input factors -1, -1, oops-> Enter 0 and get flag…
x=np.array(datas[:60]).prod() if (pow(5, x)-pow(3, x))%(pow(2, x)+115)==0: print('NHNC{FAKE_FLAG}')
else: print('The cutest lion doesn\'t like your logs...', x)
if __name__=='__main__': print(banner) logs=[] oops=random.randint(1, 2**64) print(f"Oops: {oops}") whileTrue: p=int(input("Input your number to check if it's a Lion Prime: ")) if p==0: break if lion_prime(p): print("This is a Lion Prime, prove me wrong !!!") cur=[] for i inrange(1, 4): cur.append(int(input(f"Factor {i}: "))) if cur[i-1]<=1: print("Invalid Factor!!!") exit()
if cur[0]*cur[1]*cur[2]==p: print("OK, I'll report the issue") logs.append(p^oops) else: print("WA / 0") else: print("This is not a Lion Prime QwQ...") iflen(logs)!=len(set(logs)): print("Collision!!!") exit() checker(logs)
Ok, now you can find a bunch of pseuodo primes to deceive the lion_prime tester…
Step 2.
Take a look at checker function, the x value should pass this:
1
if (pow(5, x)-pow(3, x))%(pow(2, x)+115)==0
This equaltion won’t satisfy unless x==0, I’m not going to do the math since it’s not necessary for this challenge (proof by getting the flag). But their’s a similar and detail proof here: https://artofproblemsolving.com/community/c6h3107338p28104291
But how can the x be 0 since it’s multipled by some fake primes… Wait until it’s a pseuodo prime would be a good choice but a long time lasting work
Step 3.
OVERFLOW, the max long int size in numpy is 64 bits, and in python3, the max length for an int type variable is 4300…
Bypass patch function: 4300 digits overflow, Make x=np.array(datas[:60]).prod() become 0: just make the value modulus 2^64==0
Exploit!!!
My method is generate some pseuodo primes which is not large(to make it faster in multiplication in numpy) first, and then generate some bigger one: Generator.py
from Crypto.Util.number import * from chal import lion_prime import random import json payloads=[] primes=[] cnt=0 f=open('payloads.json', 'w') whileTrue: k=random.randrange(2**13, 2**15) if isPrime(6*k+1) and isPrime(12*k+1) and isPrime(18*k+1): print(f"Found: {cnt+1}") if lion_prime((6*k+1)*(12*k+1)*(18*k+1)) and (6*k+1)*(12*k+1)*(18*k+1) notin primes: primes.append((6*k+1)*(12*k+1)*(18*k+1)) print(f"Found\np: {(6*k+1)*(12*k+1)*(18*k+1)}\nFactors:{[(6*k+1), (12*k+1), (18*k+1)]}") payloads.append({'p':(6*k+1)*(12*k+1)*(18*k+1), 'f':[(6*k+1), (12*k+1), (18*k+1)]}) cnt+=1 if cnt>=64: break
whileTrue: k=random.randrange(2**59, 2**60) if isPrime(6*k+1) and isPrime(12*k+1) and isPrime(18*k+1): print(f"Found: {cnt+1}") if lion_prime((6*k+1)*(12*k+1)*(18*k+1)) and (6*k+1)*(12*k+1)*(18*k+1) notin primes: primes.append((6*k+1)*(12*k+1)*(18*k+1)) print(f"Found\np: {(6*k+1)*(12*k+1)*(18*k+1)}\nFactors:{[(6*k+1), (12*k+1), (18*k+1)]}") payloads.append({'p':(6*k+1)*(12*k+1)*(18*k+1), 'f':[(6*k+1), (12*k+1), (18*k+1)]}) cnt+=1 if cnt>=150: break
import json from pwn import * from tqdm import tqdm from time import sleep r=remote('23.146.248.134', 11116) # r=remote('0.0.0.0', 9999) f=open('payloads.json', 'r') payloads=json.load(f) r.recvuntil(b'Lion Prime Checker') print(r.recvuntil(b'Oops')) print(r.recvline())
for pd in tqdm(payloads): r.sendlineafter(b': ', str(pd['p']).encode()) # sleep(0.3) for i inrange(3): r.sendlineafter(b': ', str(pd['f'][i]).encode()) # sleep(0.1) print(r.recvline())
r.sendlineafter(b': ', b'0')
r.interactive()
wait until the oops value is an odd one, and run the script to get flag!
if (isset($_POST['pop'])){ unserialize(base64_decode($_POST['pop'])); } ?>
The POP Chain: Trigger the chain by __destruct method in TEST -> Using the preg_match function to call __toString in MEOW -> Trigger __get in WHALE by $this->id->r3a1_name in the previous __toString Finally, SSRF in this part: