This challenge was based on a 4th degree Univariate Polynomial Ring. We are provided with lift.sage and outputs.txt. In lift.sage we have:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
flag = int(open('flag.txt','r').read().encode("hex"),16) ranges = int(log(flag,2)) p = next_prime(ZZ.random_element(2^15, 2^16)) k = 100 N = p^k d = 5 P. = PolynomialRing(Zmod(N), implementation='NTL') pol = 0 for c inrange(d): pol += ZZ.random_element(2^ranges, 2^(ranges+1))*x^c remainder = pol(flag) pol = pol - remainder assert pol(flag) == 0
It is clear from the above snippet that the flag is a root of the following polynomial: $ ax^4 + bx^3 + cx^2 + dx + e = 0;(mod;N) $ where $ N = p^{100} $; $ p = 35671 $
Thus, the challenge was to find the root of this polynomial!
To solve this challenge, we can first find a solution for the given polynomial over $ mod;p $ and then use Hensel’s lifting lemma to find a solution for the given polynomial over $ mod;p^k = mod; N $. Hensel’s lifting lemma states that:
If a polynomial equation has a simple root modulo a prime number p, then this root corresponds to a unique root of the same equation modulo any higher power of p, which can be found by iteratively “lifting” the solution modulo successive powers of p.
Step-1: Finding roots over $ mod;p $
1 2 3 4 5
p = 35671 k = 100 P. = PolynomialRing(Zmod(p), implementation='NTL') f = 12172655049735206766902704703038559858384636896299329359049381021748*x^4 + 11349632906292428218038992315252727065628405382223597973250830870345*x^3 + 9188725924715231519926481580171897766710554662167067944757835186451*x^2 + 8640134917502441100824547249422817926745071806483482930174015978801*x + 170423096151399242531943631075016082117474571389010646663163733960337669863762406085472678450206495375341400002076986312777537466715254543510453341546006440265217992449199424909061809647640636052570307868161063402607743165324091856116789213643943407874991700761651741114881108492638404942954408505222152223605412516092742190317989684590782541294253512675164049148557663016927886803673382663921583479090048005883115303905133335418178354255826423404513286728 print f.monic().roots()
Cool! This python/sagemath script returned the base solutions as [27020, 25020]
Step-2: Use Hensel’s lifting to find roots over $ mod;N $
We primarily used p4’s Hensel’s lifting implementation in their library crypto-commons. v4d3r tweaked it to make it compatible with sagemath.
from sage.allimport * from Crypto.Util.number import *
deflift(f, p, k, previous): result = [] df = diff(f) for lower_solution in previous: dfr = Integer(df(lower_solution)) fr = Integer(f(lower_solution)) if dfr % p != 0: t = (-(xgcd(dfr, p)[1]) * int(fr / p ** (k - 1))) % p result.append(lower_solution + t * p ** (k - 1)) if dfr % p == 0: if fr % p ** k == 0: for t inrange(0, p): result.append(lower_solution + t * p ** (k - 1)) return result
defhensel_lifting(f, p, k, base_solution): solution = base_solution for i inrange(2, k + 1): solution = lift(f, p, i, solution) return solution
if __name__=="__main__": #base = [27020,25020] base = [27020] p = 35671 k = 100 N = p^k P. = PolynomialRing(Zmod(N), implementation='NTL') f = 12172655049735206766902704703038559858384636896299329359049381021748*x^4 + 11349632906292428218038992315252727065628405382223597973250830870345*x^3 + 9188725924715231519926481580171897766710554662167067944757835186451*x^2 + 8640134917502441100824547249422817926745071806483482930174015978801*x + 170423096151399242531943631075016082117474571389010646663163733960337669863762406085472678450206495375341400002076986312777537466715254543510453341546006440265217992449199424909061809647640636052570307868161063402607743165324091856116789213643943407874991700761651741114881108492638404942954408505222152223605412516092742190317989684590782541294253512675164049148557663016927886803673382663921583479090048005883115303905133335418178354255826423404513286728 solutions = hensel_lifting(f,p,k,base) for solution in solutions: print long_to_bytes(solution)
Running the above script gave the flag as: p4{Th4t5_50m3_h34vy_l1ft1n9}