On nous propose un programme PYTHON un peu inhabituel.
Ci joint une version simplifiée (j’ai simplement mis une seule lambda fonction dans L au lieu des 38 de l’énoncé) :
L = [
lambda x: 7356954580121243762*x**16 - 67917758999022629485319994263285229323*x**15 - 126557346997785504703303593285520174611*x**14 - 79196433701250933379099644305803773692*x**13 + 64127295017451262308591178117921719607*x**12 + 30348988932379286893278017713168381811*x**11 + 103217349796193762180113460134492309958*x**10 - 154876828044950977759573152639253989872*x**9 - 83291919960546378198292499838313325081*x**8 + 13843350859630151992361239852646634059*x**7 - 102720501418550210355972956242885213268*x**6 - 155819885470062394096388278625509744351*x**5 - 71889517112932522335238158706394974633*x**4 - 102947773924158754256136745869287671436*x**3 - 123078785304424416159367972716706676890*x**2 + 52344605551728127581536635264341776866*x + 142567267056755772849168679088948045314,
]
try:
flag = []
for y in L:
this = int(input(">>> "))
fine = int(input(">>> "))
assert this == fine
this = y(this)
fine = y(fine)
assert this is fine
flag.append(this | fine)
print(bytes(flag).decode())
except:
print("Nope!")
On nous demande assez bizarrement deux entrées « this » et « fine » qui
doivent être égales (test assert avec l’opérateur d’égalité ==).
Bon, a priori cela ne sert pas à grand chose. Sauf à attirer notre attention
sur l’opérateur de comparaison.
Ensuite on calcule la valeur de la lambda fonction (ici chaque lambda fonction sera un polynôme de degré 16) pour « this » et « fine » ce qui en toute logique conduit à deux valeurs égales puisque les arguments sont finalement aussi égaux.
Mais le test du assert ne consiste pas en un test d’égalité mais en
un test d’identicité via l’opérateur is. En PYTHON, cet opérateur
renvoie true si deux variables font en fait référence à la même zone
mémoire, au même objet. Par exemple le test est true ici :
a = [1, 2, 3]
b = a
assert a is b
En toute logique, il semble peu crédible ici que le calcul de la lambda fonction renvoie la même référence d’objet résultat pour deux calculs de suite.
Mais en lisant de la documentation sur le fonctionnement de l’opérateur
is, on apprend que l’on pourra avoir une identicité si l’objet calculé
est « court », cette définition de « court » étant assez dépendante de
la version de PYTHON. Par contre, on semble s’accorder pour dire que si
le calcul renvoie un « petit » entier alors on pourra avoir une
identicité. Petit signifie ici souvent entre 0 et 256.
Cela tombe bien parce que le reste du programme fait :
flag = []
for y in L:
...
this = y(this)
fine = y(fine)
assert this is fine
flag.append(this | fine)
print(bytes(flag).decode())
On accumule donc les valeurs calculées qu’on convertira ensuite de leurs valeurs ASCII en caractères.
On peut donc affiner un peu plus les choses. On devra avoir une petite valeur entre 0x20 et 0x7d (classiquement les flags du FCSC sont constitués de caractères dans cet intervalle ASCII).
La partie this | fine est de l’intox pour nous impressionner car
l’identicité fait qu’on OR deux valeurs identiques entre elles.
Résumons le problème :
- pour chaque polynôme P dans la liste L,
- on veut une valeur a entière en entrée
- a est tel que P(a) est dans l’intervalle
[0x20, 0x7d] - dit autrement a est racine de
P(x) - f = 0avec f dans[0x20, 0x7d]
Comme le challenge se résoud, qu’il se résoud dans ambiguité, on se
doute que chaque caractère f du flag est tel que P(x) - f se
factorise en un monôme de degré 1 de type X - a et d’un autre
polynôme irréductible (donc sans racine entière) de degré 15.
Dit encore autrement, pour chaque polynôme de la liste L, on cherche
donc les f permettant de rendre P(x) - f non irréductible.
D’où mon script de solution en PARI/GP (script complet ici - ci-dessous je donne une version abrégée):
L = [ \
7356954580121243762*x^16 - 67917758999022629485319994263285229323*x^15 - 126557346997785504703303593285520174611*x^14 - 79196433701250933379099644305803773692*x^13 + 64127295017451262308591178117921719607*x^12 + 30348988932379286893278017713168381811*x^11 + 103217349796193762180113460134492309958*x^10 - 154876828044950977759573152639253989872*x^9 - 83291919960546378198292499838313325081*x^8 + 13843350859630151992361239852646634059*x^7 - 102720501418550210355972956242885213268*x^6 - 155819885470062394096388278625509744351*x^5 - 71889517112932522335238158706394974633*x^4 - 102947773924158754256136745869287671436*x^3 - 123078785304424416159367972716706676890*x^2 + 52344605551728127581536635264341776866*x + 142567267056755772849168679088948045314, \
...
] ;
foreach(L, l, \
for(f = 0x20, 0x7d, \
if( ! polisirreducible(l - f), \
printf("%c", f); \
break ; \
) \
) \
)
printf("\n") ;
quit ;
Je ne cherche pas à connaitre les entiers à entrer puisque la valeur
qui nous intéresse est f.
L’exécution du script donne :
$ gp solve.gp
FCSC{dqcb7dVhWa9hunProTfJMsurwboPkT3V}
Bingo. Ce flag se valide.
QED.
🐒