I participated in idek’s 2021 CTF, playing as part of Social Engineering Experts over the weekend (Sat, 11 Dec. 2021, 08:00 SGT — Mon, 13 Dec. 2021, 08:00 SGT). In the end, we ranked 13th out of 235 scoring teams. Swept all crypto :
Below are the writeups :
Challenge | Category | Points | Solves |
---|---|---|---|
Idek ExponEntial Extravaganza | Rev | 498 | 5 |
EccRoll | Crypto | 484 | 14 |
Destroyted RSA | Crypto | 480 | 16 |
Polyphenol | Crypto | 465 | 21 |
Seed of Life | Crypto | 423 | 31 |
Hasbrown | Crypto | 338 | 45 |
Nameless | Crypto | 172 | 64 |
Rotting Fruits | Crypto | 100 | 178 |
Sanity Check | Misc | 100 | 193 |
Note : Some people solved the challenges and submitted the flag after the CTF ended. The points and solves shown above are the real numbers for solves during the duration of the CTF.
Idek ExponEntial Extravaganza
Source Code :
#include <stdio.h>
#include <string.h>
#include <math.h>
int main(int argc, char** argv) {
if(argc != 2){
printf("Usage: ./reverseme password\n");
return 1;
}
if(strlen(argv[1])!=14){
printf("Incorrect Length\n");
return 1;
}
if(*argv[1] != 112){//Not enough precision
printf("Password Incorrect\n");
return 1;
}
double magic_numbers[7] ={-68822144.50341525673866271972656250000000000000000000000000, 56777293.39031631499528884887695312500000000000000000000000, -3274524.75536667229607701301574707031250000000000000000000, -85761.51255339206545613706111907958984375000000000000000, 8443.33244327564352715853601694107055664062500000000000, -166.67369627952575683593750000000000000000000000000000, 1.00000000000000000000000000000000000000000000000000, };
for(int i = 0; i < 6;i++){
double foo=1.0,bar=0.0;
for(int j=0;j<7;j++){
bar += magic_numbers[j] * foo;
foo *= (float)log(*(float*)((unsigned long)argv[1]+2*i));
}
if((int)(100000*bar) != 0){
printf("Password Incorrect\n");
return 1;
}
}
printf("Password Correct\n");
return 0;
}
Solve scripts :
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main(void) {
printf("[");
double magic_numbers[7] ={-68822144.50341525673866271972656250000000000000000000000000, 56777293.39031631499528884887695312500000000000000000000000, -3274524.75536667229607701301574707031250000000000000000000, -85761.51255339206545613706111907958984375000000000000000, 8443.33244327564352715853601694107055664062500000000000, -166.67369627952575683593750000000000000000000000000000, 1.00000000000000000000000000000000000000000000000000, };
for (char c1 = 32; c1 < 127; c1++) {
for (char c2 = 32; c2 < 127; c2++) {
for (char c3 = 32; c3 < 127; c3++) {
for (char c4 = 32; c4 < 127; c4++) {
char s[] = {c1, c2, c3, c4, 0};
for(int i = 0; i < 6;i++){
double foo=1.0,bar=0.0;
for(int j=0;j<7;j++){
bar += magic_numbers[j] * foo;
foo *= (float)log(*(float*)((unsigned long)s+2*i));
}
if((int)(100000*bar) != 0){
continue;
}
else {
printf("b'%s', ", s);
}
}
}
}
}
}
printf("]");
}
Output of above solver stored in this file.
table = [b'$Yn@', b'%Yn@', b'/fTw', b'0fTw', b'1fTw', b'21aL', b'2fTw', b'31aL', b'3fTw', b'41aL', b'4fTw', b'51aL', b'5fTw', b'61aL', b'6fTw', b'71aL', b'7fTw', b'81aL', b'8fTw', b'91aL', b'9fTw', b':1aL', b':fTw', b';1aL', b';fTw', b'<1aL', b'<fTw', b'=1aL', b'=fTw', b'>1aL', b'>fTw', b'?1aL', b'?fTw', b'@1aL', b'@fTw', b'A1aL', b'AfTw', b'B1aL', b'BfTw', b'C1aL', b'CfTw', b'D1aL', b'DfTw', b'E1aL', b'EfTw', b'F1aL', b'FfTw', b'G1aL', b'GfTw', b'H1aL', b'HfTw', b'I1aL', b'IfTw', b'J1aL', b'JfTw', b'K1aL', b'KfTw', b'L1aL', b'LfTw', b'M1aL', b'MfTw', b'NfTw', b'OfTw', b'PfTw', b'QfTw', b'RfTw', b'SLzf', b'SfTw', b'TLzf', b'TfTw', b'ULzf', b'UfTw', b'VLzf', b'VfTw', b'WLzf', b'WfTw', b'XLzf', b'XfTw', b'YLzf', b'YfTw', b'ZLzf', b'ZfTw', b'[Lzf', b'[fTw', b'\Lzf', b'\fTw', b']Lzf', b']fTw', b'^Lzf', b'^fTw', b'_Lzf', b'_fTw', b'`Lzf', b'`fTw', b'a0%Y', b'aLzf', b'afTw', b'b0%Y', b'bLzf', b'bfTw', b'c0%Y', b'cLzf', b'cfTw', b'd0%Y', b'dLzf', b'dfTw', b'e0%Y', b'eLzf', b'efTw', b'f0%Y', b'fLzf', b'ffTw', b'g0%Y', b'gLzf', b'gfTw', b'h0%Y', b'hLzf', b'hfTw', b'i0%Y', b'iLzf', b'ifTw', b'j0%Y', b'jLzf', b'jfTw', b'k0%Y', b'kLzf', b'kfTw', b'l0%Y', b'lLzf', b'lfTw', b'm0%Y', b'm@M1', b'mLzf', b'mfTw', b'n0%Y', b'n@M1', b'nLzf', b'nfTw', b'o0%Y', b'o@M1', b'oLzf', b'ofTw', b'p0%Y', b'p@M1', b'pLzf', b'pfTw', b'q0%Y', b'q@M1', b'qLzf', b'qfTw', b'r0%Y', b'r@M1', b'rLzf', b'rfTw', b's0%Y', b's@M1', b'sLzf', b'sfTw', b't0%Y', b't@M1', b'tLzf', b'tfTw', b'u0%Y', b'u@M1', b'uLzf', b'ufTw', b'v0%Y', b'v@M1', b'vLzf', b'vfTw', b'w0%Y', b'w@M1', b'wLzf', b'wfTw', b'x0%Y', b'x@M1', b'xLzf', b'xfTw', b'y0%Y', b'y@M1', b'yLzf', b'yfTw', b'z0%Y', b'z@M1', b'zLzf', b'zfTw', b'{0%Y', b'{@M1', b'{Lzf', b'{fTw', b'|0%Y', b'|@M1', b'|Lzf', b'|fTw', b'}0%Y', b'}@M1', b'}Lzf', b'}fTw', b'~0%Y', b'~@M1', b'~Lzf', b'~fTw', ]
print(len(table))
print([x for x in table if x[0] == 112])
print([x for x in table if x[:2] == b"%Y"])
print([x for x in table if x[:2] == b"n@"])
print([x for x in table if x[:2] == b"M1"])
print([x for x in table if x[:2] == b"aL"])
print([x for x in table if x[:2] == b"zf"])
print(b"idek{" + b'p0%Y' + b"n@" + b"M1" + b"aL" + b"zfTw" + b"}")
Flag : idek{p0%Yn@M1aLzfTw}
EccRoll
Source code :
from Crypto.Util.number import bytes_to_long
import random
from secret import flag
def gen(nbits):
p = random_prime(2^(nbits)+1, 2^(nbits))
E = EllipticCurve(GF(p), [9487, 0])
G = E.gens()[0]
ord_G = G.order()
### Remove small prime powers to avoid Pohlig-Hellman
for i in range(2, 33):
if ord_G % i == 0:
G = i * G
ord_G //= i
### Make a relationship between generators, so it will
### be much harder to guess the bits, right?
g = (p - G.xy()[0])
return p, G, g
def encrypt(bflag):
p, G, g = gen(128)
enc = []
for b in bflag:
### Haha, the outputs should be random enough
r = random.randint(2, p-1)
if b == "0":
enc += [(r * G).xy()[0]]
else:
enc += [pow(g, r, p)]
return p, G, g, enc
bflag = bin(bytes_to_long(flag))[2:]
### I'm so kind, thus I'll give you 20 encryptions
for i in range(20):
p, G, g, enc = encrypt(bflag)
print("p = {}".format(p))
print("G = {}".format(G))
print("g = {}".format(g))
print("enc = {}".format(enc))
Output file can be found here.
Solve script :
from tqdm import tqdm
from Crypto.Util.number import long_to_bytes
p_list = []
G_list = []
g_list = []
enc_list = []
with open('output.txt', 'r') as f:
lines = f.readlines()
for i, line in enumerate(lines):
#p
if i % 4 == 0:
p_list.append(Integer(line.strip().split(" = ")[1]))
#G
if i % 4 == 1:
F = EllipticCurve(GF(p_list[-1]), [9487, 0])
comps = line.strip().split(" = ")[1].split(" : ")
comps = [x.replace("(", "").replace(")", "") for x in comps]
G_list.append(F(Integer(comps[0]), Integer(comps[1])))
#g
if i % 4 == 2:
g_list.append(Integer(line.strip().split(" = ")[1]))
#enc
if i % 4 == 3:
str_list = line.strip().split(" = ")[1][1:-1].split(", ")
enc_list.append([Integer(x) for x in str_list])
def guessBit(p, x):
return str(int(kronecker(x, p) == -1))
guesses = []
for guess in guesses:
print(''.join(guess))
for p, G, g, enc in list(zip(p_list, G_list, g_list, enc_list)):
E = EllipticCurve(GF(p), [9487, 0])
guesses.append([])
for bit in enc:
guess = guessBit(p, bit)
guesses[-1].append(guess)
to_remove = []
for i, guess in enumerate(guesses):
if '1' not in guess:
to_remove.append(i)
for i, ind in enumerate(to_remove):
del guesses[ind - i]
compounded = []
for i in range(len(guesses[0])):
for guess in guesses:
if guess[i] == '1':
compounded.append('1')
break
else:
compounded.append('0')
print(long_to_bytes(int(''.join(compounded), 2)))
#b'idek{Wh3n_b=0_X_C00rd1n4t3s_of_p01nts_w1th_0dd_0rd3rs_4r3_qu4dr4t1c_r3s1du3s!!!}'
Flag : idek{Wh3n_b=0_X_C00rd1n4t3s_of_p01nts_w1th_0dd_0rd3rs_4r3_qu4dr4t1c_r3s1du3s!!!}
Destroyed RSA
Source code :
import random
from Crypto.Util.number import bytes_to_long, getPrime, isPrime
from flag import flag
f = flag
def interesting_prime():
#recognize me?
D = 987
while True:
s = random.randint(2**1020,2**1021-1)
check = D * s ** 2 + 1
if check % 4 == 0 and isPrime((check // 4)):
return check // 4
m = bytes_to_long(f)
p = interesting_prime()
q = getPrime(2048)
N = p*q
e = 6551
c = pow(m, e, N)
with open('out.txt', 'w') as w:
w.write(f"n = {N}")
w.write(f"e = {e}")
w.write(f"c = {c}")
Output file can be found here.
Solve script :
from sage.all import *
from math import gcd
import sys
from Crypto.Util.number import *
from tqdm import tqdm
sys.setrecursionlimit(100000)
def polynomial_xgcd(a, b):
"""
Computes the extended GCD of two polynomials using Euclid's algorithm.
:param a: the first polynomial
:param b: the second polynomial
:return: a tuple containing r, s, and t
"""
assert a.base_ring() == b.base_ring()
r_prev, r = a, b
s_prev, s = 1, 0
t_prev, t = 0, 1
while r:
try:
q = r_prev // r
r_prev, r = r, r_prev - q * r
s_prev, s = s, s_prev - q * s
t_prev, t = t, t_prev - q * t
except RuntimeError:
raise ArithmeticError("r is not invertible", r)
return r_prev, s_prev, t_prev
def polynomial_inverse(p, m):
"""
Computes the inverse of a polynomial modulo a polynomial using the extended GCD.
:param p: the polynomial
:param m: the polynomial modulus
:return: the inverse of p modulo m
"""
g, s, t = polynomial_xgcd(p, m)
return s * g.lc() ** -1
def factorize(N, D):
"""
Recovers the prime factors from a modulus using Cheng's elliptic curve complex multiplication method.
More information: Sedlacek V. et al., "I want to break square-free: The 4p - 1 factorization method and its RSA backdoor viability"
:param N: the modulus
:param D: the discriminant to use to generate the Hilbert polynomial
:return: a tuple containing the prime factors
"""
assert D % 8 == 3, "D should be square-free"
zmodn = Zmod(N)
pr = zmodn["x"]
H = pr(hilbert_class_polynomial(-D))
Q = pr.quotient(H)
j = Q.gen()
try:
k = j * polynomial_inverse((1728 - j).lift(), H)
except ArithmeticError as err:
# If some polynomial was not invertible during XGCD calculation, we can factor n.
p = gcd(int(err.args[1].lc()), N)
return int(p), int(N // p)
E = EllipticCurve(Q, [3 * k, 2 * k])
while True:
x = zmodn.random_element()
#print(f"Calculating division polynomial of Q{x}...")
z = E.division_polynomial(N, x=Q(x))
try:
d, _, _ = polynomial_xgcd(z.lift(), H)
except ArithmeticError as err:
# If some polynomial was not invertible during XGCD calculation, we can factor n.
p = gcd(int(err.args[1].lc()), N)
return int(p), int(N // p)
p = gcd(int(d), N)
if 1 < p < N:
return int(p), int(N // p)
n = 1107089530865291005792928480548189885479977703442107533313356742591951667125087535826010797157037462511849114045516095117408586807231706532861043400760247491545435256670371588020835746662537074526723641129179212527096161455117334264697975671817050553066298766943783138197527420889207777538892682506714149210612017224796083819420558023833783052917520724666512823601961316820680461331809092698744718706733677296285030329390980535993866209005527261307419128592439300515586162373258781346476786339580690681056167085317745381659204404735103663112312693895151699459534695810700103198736323229849182269856798173444808702578924983265251814786149856862410628895786620259450924651662102926294106305830166586486290823127164862167085238586221495862582249250498351063486438791642573599997108715545357001141390463446119948917429169048381714872075664706511363903028841832059662969788526342879432038294204060254076089346916015904423832151508649589004393115281760187305175538986217342069866841444503926477979016789070563231454129660241269241703644048193901022392096029852555194381071481792694321652042639278577632914177886450520371875549477493674646527815335341293227066407822982856815277497965140254717599437693337147736442753971955722420621907486011
e = 6551
c = 1058973149865164549817155137780815812623456388672543624635614085499244505987561039674767711743995552767627702752408380616883846211075499731545623651849875151404363710832001498313789374024771997636035858261588415813326816005995068929381352608925142940988357078368871961040798086164564448858106053108740194519944625550651430368864502736439474454098320996590422347650921528404245531928248700516748854749201075928521539792773428322450795957658901093109369813686410937987553062385702498313602204488073904351501964183374202465473380985602391885101503608627656341003137115517005592591746184951205036757837506166599417314523057848473058411480339477693062957272869601856015496182168433136199994935250109784152100245527161736252507193119985157036229139371883973535111593404077185046749750799222687057579590699112612517984739574870394435873979331373499970168615775526148858445852147161884965435806142797327740412603655959161046424598064190354357612721920648035385092037901968197029138094288551013982417040611621259974923351358663093743109476722870043782424356300204605852351738436494837101112320000689343206094235482366575795725469918145189121430260345459865260920783660140849521053205162497433393126952425091234633490186637000777737106192645410
D = 987
p, q = factorize(n, D)
assert p*q == n
def roots_of_unity(e, phi, n, rounds=250):
# Divide common factors of `phi` and `e` until they're coprime.
phi_coprime = phi
while gcd(phi_coprime, e) != 1:
phi_coprime //= gcd(phi_coprime, e)
# Don't know how many roots of unity there are, so just try and collect a bunch
roots = set(pow(i, phi_coprime, n) for i in range(1, rounds))
assert all(pow(root, e, n) == 1 for root in roots)
return roots, phi_coprime
# n is prime
# Problem: e and phi are not coprime - d does not exist
phi = (p - 1) * (q-1)
# Find e'th roots of unity modulo n
roots, phi_coprime = roots_of_unity(e, phi, n)
# Use our `phi_coprime` to get one possible plaintext
d = inverse_mod(e, phi_coprime)
pt = pow(c, d, n)
assert pow(pt, e, n) == c
# Use the roots of unity to get all other possible plaintexts
pts = [(pt * root) % n for root in roots]
pts = [long_to_bytes(pt) for pt in pts]
for pt in pts:
if b'idek' in pt:
print(pt)
#b'idek{A_mashup_of_2_interesting_papers?_4p-1_and_coprime_e-phi}'
Got first blood :D
Flag : idek{A_mashup_of_2_interesting_papers?_4p-1_and_coprime_e-phi}
Polyphenol
Source code :
import random
from secret import flag
assert flag[: 5] == b"idek{"
assert flag[-1:] == b"}"
L = len(flag[5: -1])
print(f"L = {L}")
coeff = list(flag[5: -1])
points = random.sample(range(L), L // 2)
evaluations = []
for p in points:
evaluations += [sum(c * p ** i for i, c in enumerate(coeff))]
print(f"points = {points}")
print(f"evaluations = {evaluations}")
Output file can be found here.
Solve script :
from sage.modules.free_module_integer import IntegerLattice
# Directly taken from rbtree's LLL repository
# From https://oddcoder.com/LOL-34c3/, https://hackmd.io/@hakatashi/B1OM7HFVI
def Babai_CVP(mat, target):
M = IntegerLattice(mat, lll_reduce=True).reduced_basis
G = M.gram_schmidt()[0]
diff = target
for i in reversed(range(G.nrows())):
diff -= M[i] * ((diff * G[i]) / (G[i] * G[i])).round()
return target - diff
def solve(mat, lb, ub, weight = None):
num_var = mat.nrows()
num_ineq = mat.ncols()
max_element = 0
for i in range(num_var):
for j in range(num_ineq):
max_element = max(max_element, abs(mat[i, j]))
if weight == None:
weight = num_ineq * max_element
# sanity checker
if len(lb) != num_ineq:
print("Fail: len(lb) != num_ineq")
return
if len(ub) != num_ineq:
print("Fail: len(ub) != num_ineq")
return
for i in range(num_ineq):
if lb[i] > ub[i]:
print("Fail: lb[i] > ub[i] at index", i)
return
# heuristic for number of solutions
DET = 0
if num_var == num_ineq:
DET = abs(mat.det())
num_sol = 1
for i in range(num_ineq):
num_sol *= (ub[i] - lb[i])
if DET == 0:
print("Zero Determinant")
else:
num_sol //= DET
# + 1 added in for the sake of not making it zero...
print("Expected Number of Solutions : ", num_sol + 1)
# scaling process begins
max_diff = max([ub[i] - lb[i] for i in range(num_ineq)])
applied_weights = []
for i in range(num_ineq):
ineq_weight = weight if lb[i] == ub[i] else max_diff // (ub[i] - lb[i])
applied_weights.append(ineq_weight)
for j in range(num_var):
mat[j, i] *= ineq_weight
lb[i] *= ineq_weight
ub[i] *= ineq_weight
# Solve CVP
target = vector([(lb[i] + ub[i]) // 2 for i in range(num_ineq)])
result = Babai_CVP(mat, target)
for i in range(num_ineq):
if (lb[i] <= result[i] <= ub[i]) == False:
print("Fail : inequality does not hold after solving")
break
# recover x
fin = None
if DET != 0:
mat = mat.transpose()
fin = mat.solve_right(result)
## recover your result
return result, applied_weights, fin
L = 34
points = [4, 26, 22, 30, 1, 0, 2, 28, 23, 6, 25, 15, 5, 17, 14, 3, 13]
evaluations = [6218619148819094267912, 3239660278168289094170378865781537878483145039862, 13167423991006904868698304825721530103488567362, 362300164581366743933077318596814390341728710066538, 2912, 68, 1115908868222, 37269531510352347514290324712333731253807861642016, 56973337294691266392513915302684316231304471586, 3613524058959314538010460402, 889396341293302641051808753025749133146757798168, 43685960244566626421146033917835089685178, 9183044381254551882537188, 2694648608589552169772471076958756166751152, 4505915953382938147124977926390455694362, 529597204775372366, 392892826701163426612412172019222254476]
M1 = identity_matrix(L)
M2 = Matrix([list(map(lambda x: x^i, points)) for i in range(L)])
M = M1.augment(M2)
lb = [32] * L + evaluations
ub = [126] * L + evaluations
res, weights, _ = solve(M, lb, ub)
print("idek{", ''.join([chr(x) for x in res[:L]]), "}", sep="")
#idek{D1d_y0u_s34rch_7h1s_p0ly_6y_LLL???}
Flag : idek{D1d_y0u_s34rch_7h1s_p0ly_6y_LLL???}
Seed of Life
Source code :
import random
seed = REDACTED
assert seed in range(10000000)
random.seed(seed)
for i in range(19):
random.seed(random.random())
seedtosave = random.random()
print("share1:")
for add in range(0, 1000):
random.seed(seedtosave+add)
for i in range(0, 100):
print(random.random())
print("share2:")
for add in range(0, 1000):
random.seed(seedtosave-add)
for i in range(0, 1000):
print(random.random())
print("share3:")
random.seed(seedtosave)
for i in range(0, 100):
print(random.random()*100)
Output file can be found here.
Solve script :
import random
from tqdm import tqdm
from Crypto.Util.number import *
for seed in tqdm(range(10000000)):
random.seed(seed)
toBreak = False
for i in range(19):
random.seed(random.random())
seedtosave = random.random()
for add in range(0, 1000):
random.seed(seedtosave+add)
for i in range(0, 100):
temp = random.random()
if add == 0 and i == 0 and temp != 0.5327486342598738:
toBreak = True
break
if toBreak:
break
if toBreak:
continue
for add in range(0, 1000):
random.seed(seedtosave-add)
for i in range(0, 1000):
random.random()
random.seed(seedtosave)
for i in range(0, 100):
t = random.random()*100
if t == 83.74981977975804:
print("idek{", seed, "}", sep="")
exit()
#idek{103123}
Flag : idek{103123}
Hashbrown
Source code :
import string
import hashlib
import random
password2hash = b"REDACTED"
hashresult = hashlib.md5(password2hash).digest()
sha1 = hashlib.sha1(hashresult)
sha224 = hashlib.sha224(sha1.digest())
for i in range(0, 10):
sha1 = hashlib.sha1(sha224.digest())
sha224 = hashlib.sha224(sha1.digest())
output = sha224.hexdigest()
print("output: " + output)
Output file can be found here.
Solve script :
import string
import hashlib
import random
from tqdm import tqdm
possibleChars = list(chr(i).encode() for i in range(32, 127))
for c1 in tqdm(possibleChars):
for c2 in possibleChars:
for c3 in possibleChars:
for c4 in possibleChars:
password2hash = c1 + c2 + c3 + c4
hashresult = hashlib.md5(password2hash).digest()
sha1 = hashlib.sha1(hashresult)
sha224 = hashlib.sha224(sha1.digest())
for i in range(0, 10):
sha1 = hashlib.sha1(sha224.digest())
sha224 = hashlib.sha224(sha1.digest())
output = sha224.hexdigest()
if output == "9ee2275f8699c3146b65fabc390d83df5657a96c39ab58933f82d39b":
print("idek{", password2hash.decode(), "}", sep="")
#idek{WDOb}
exit()
Flag : idek{WDOb}
Nameless
Source code :
#!/usr/bin/env python3
from Crypto.Util.number import getPrime, bytes_to_long
flag = bytes_to_long(open("flag.txt", "rb").read())
p = getPrime(1024)
q = getPrime(1024)
n = p*q
e = 65537
c = pow(flag, e, n)
print(f"{n = }")
print(f"{p**2 + q**2 = }")
print(f"{c = }")
# n = 17039353907577304435335064263404014554877715060984532599266619880863167873378099082282744647069063737519071263364836126585022715467571812084451441982393173641398961475914685815327955647115633127041896154455593434072255425400800779717723399468604805292082232853055652824142503280033249169812067036520117578584094798348819948005306782099055133323817492597665553443090585282100292603079932759878536941929823231580881942192749039900111873581375554659251791337260557811529597205007196563571790350676229812320194120553090511341491088451472118285832059742983329898372623700182290118257197824687682775782009980169859003817731
# p**2 + q**2 = 34254734236141177160574679812056859631858427160408786991475995766265871545173190051194038767461225382849521482292062983459474860288453334280315736001800236347672807900333594896297515619502911996316514299218938831378736595562870019767614772735193898275208842936903810908125651716713945099823849942766283224215669363078687494444967371294251548767512167452469907361824731739495988324619487099803563636546009036759134670516039262088500254966964852889263176272377467365967151127628965809347292638988052064278479647751273833336918088826074446862207626964731876317800211831559603043730904022957158490478667914769698472788362
# c = 12870370380105677159569686874593314643716517767455659912764832987663831817849402722874771360315463499459803247514426078866675686952348433836656840934671927466173330528381359767745015167610939855705805470288376941237662107279159556248387485524451540986787953598577323572841487131458590546170321983597795128547549803960136942090569419458036728363613060710384550676895546741408072019046530957103700345379626982758919062223712005709765751343132802610106335253368313457365776378662756844353849622352138042802036310704545247436297860319183507369367717753569233726139626694256257605892684852784606001755037052492614845787835
Solve script :
from Crypto.Util.number import *
n = 17039353907577304435335064263404014554877715060984532599266619880863167873378099082282744647069063737519071263364836126585022715467571812084451441982393173641398961475914685815327955647115633127041896154455593434072255425400800779717723399468604805292082232853055652824142503280033249169812067036520117578584094798348819948005306782099055133323817492597665553443090585282100292603079932759878536941929823231580881942192749039900111873581375554659251791337260557811529597205007196563571790350676229812320194120553090511341491088451472118285832059742983329898372623700182290118257197824687682775782009980169859003817731
p2q2 = 34254734236141177160574679812056859631858427160408786991475995766265871545173190051194038767461225382849521482292062983459474860288453334280315736001800236347672807900333594896297515619502911996316514299218938831378736595562870019767614772735193898275208842936903810908125651716713945099823849942766283224215669363078687494444967371294251548767512167452469907361824731739495988324619487099803563636546009036759134670516039262088500254966964852889263176272377467365967151127628965809347292638988052064278479647751273833336918088826074446862207626964731876317800211831559603043730904022957158490478667914769698472788362
c = 12870370380105677159569686874593314643716517767455659912764832987663831817849402722874771360315463499459803247514426078866675686952348433836656840934671927466173330528381359767745015167610939855705805470288376941237662107279159556248387485524451540986787953598577323572841487131458590546170321983597795128547549803960136942090569419458036728363613060710384550676895546741408072019046530957103700345379626982758919062223712005709765751343132802610106335253368313457365776378662756844353849622352138042802036310704545247436297860319183507369367717753569233726139626694256257605892684852784606001755037052492614845787835
pPlusq = sqrt(p2q2 + 2*n)
P.<x> = PolynomialRing(ZZ, 'x')
quad = x^2 - pPlusq*x + n
roots = quad.roots()
p, q = int(roots[0][0]), int(roots[1][0])
assert p*q == n
e = 65537
phi = (p-1)*(q-1)
d = inverse(e, phi)
m = pow(c, d, n)
print(long_to_bytes(int(m)))
#b'idek{crypt0_1s_just_m4th_23b984a1}\n'
Flag : idek{crypt0_1s_just_m4th_23b984a1}
Rotting Fruits
Flag : idek{rot13_is_kinda_kewl_ngl}
Sanity Check
Flag : idek{let_the_games_begin!}