2025 Winter Challenge: Quinindrome

Rédigé par Timothée Schneider-Maunoury - 01/12/2025 - dans Challenges - Téléchargement

Quelques mois se sont écoulés et les premiers flocons sont tombés depuis la fin du Synacktiv Summer Challenge. Cet évènement avait beaucoup plu, un des participants avait même trouvé une 0day en travaillant sur sa solution ! Celle-ci n'a pas encore été rendue publique, mais fera l'objet d'un prochain article sur le site de Synacktiv. Avec l'arrivée de l'hiver, c'est maintenant le moment de vous présenter le Synacktiv Winter Challenge ! Tentez de vous mesurer aux autres participants sur ce défi de code golfing et envoyez nous votre solution avant le 1 janvier 🏌️.

Vous souhaitez améliorer vos compétences ? Découvrez nos sessions de formation ! En savoir plus

🎁 Les prix

Voici les lots pour les trois premières personnes dans le haut du classement :

  1. première place : cet excellent pack IFixIt ainsi qu'un fer à souder pour réparer toute votre électronique,
  2. seconde place : ce switch Netgear manageable 8 ports PoE+ idéal pour votre réseau domestique,
  3. troisième place : une Yubikey 5C NFC qui vous garantira une authentification sans risques !

🏆 Le challenge

L'objectif est de concevoir un quinindrome, c'est-à-dire un binaire ELF qui respecte les deux propriétés suivantes :

1. être un palindrome, donc être parfaitement symétrique,
2. et être un byte-wise quine : afficher son propre fichier sur stdout lorsqu'on l'exécute.

Bien entendu, le processus doit se finir sans segfault, et le code de retour doit être défini à 0.

Les participants au précédent challenge reconnaîtront le thème, mais ne vous y méprenez pas : vous allez devoir imaginer des techniques très différentes pour réussir à optimiser au maximum votre solution. Cette fois, il s'agira de manipuler le header d'un fichier ELF et de trouver la disposition optimale des instructions x86 qui composeront votre programme !

Voici le script de test : 

#!/bin/bash

##### Argument checks #####
# Check if binary path is provided
if [ $# -ne 1 ]; then
    echo "[+] Usage: $0 <binary_path>"
    exit 1
fi

binary=$1

# Check if file exists and is readable
if [ ! -f "$binary" ] || [ ! -r "$binary" ]; then
    echo "[!] Error: File '$binary' does not exist or is not readable."
    exit 1
fi


##### First check: byte-wise palindrome #####
reversed_file=$(mktemp)
size=$(wc -c < "$binary")

# Read the file byte by byte in reverse order (in a very efficient way)
for ((i = size - 1; i >= 0; i--)); do
    dd if="$binary" bs=1 skip=$i count=1 2>/dev/null
done > "$reversed_file"

if cmp -s "$binary" "$reversed_file"; then
    echo "[+] First check passed: binary is a byte-wise palindrome."
    rm "$reversed_file"
else
    echo "[!] First check failed: binary is not a byte-wise palindrome."
    rm "$reversed_file"
    exit 1
fi


##### Build a scratch Podman image with the binary to test #####
# Create the containerfile
image_name="quinindrome_test"
containerfile=$(mktemp)

cat > "$containerfile" <<EOF
FROM scratch
COPY $binary /binary
CMD ["/binary"]
EOF

# Build the image
if ! podman build . -t "$image_name" -f "$containerfile" >/dev/null; then
    echo "[!] Failed to build Podman test image."
    rm "$containerfile"
    exit 1
fi
rm "$containerfile"


##### Second check: quine property #####
output_file=$(mktemp)
max_run_time=120

# Run the binary in the scratch container and capture output & return code
timeout "$max_run_time" podman run --rm "$image_name" > "$output_file"
return_code=$?

if [ $return_code -ne 0 ]; then
    echo "[!] Second check failed: binary execution returned non-zero status: $return_code."
    rm "$output_file"
    exit 1
fi

if cmp -s "$binary" "$output_file"; then
    echo "[+] Second check passed: binary is a true quine, its output matches itself."
    rm "$output_file"
else
    echo "[!] Second check failed: Is that a quine? Binary output does not match itself."
    rm "$output_file"
    exit 1
fi

echo "[+] Both checks passed: your binary is a very nice quinindrome!"
echo "[+] Your score: $size"

 

📩 Modalités de participation

Les solutions seront acceptées jusqu'au 31 décembre à 23h59 UTC+1.
Pour proposer la vôtre, envoyez-la à l'adresse winter-challenge@synacktiv.com.

  1. Vous l'aurez compris, le gagnant sera celui qui parviendra à obtenir le plus petit score calculé par le script de test ci-dessus.
  2. Le challenge se déroulera pendant le mois de décembre et le write-up sera publié début 2026.
  3. Vous pouvez soumettre vos solutions au fur et à mesure de votre progression.
  4. Vous pouvez nous en envoyer autant que vous le souhaitez dans la limite d'une par jour. Ce qui signifie que si vous envoyez un binaire le 4 janvier matin vous n'aurez ensuite plus le droit à une chance supplémentaire.
  5. Une fois votre proposition reçue et validée, le classement général sera mis à jour. Cependant, les scores ne seront pas divulgués afin de préserver le suspens.
  6. Si vous avez un doute ou une question sur le règlement, n'hésitez pas à nous contacter par mail.

 

🖥️ La VM de test

Voici une description de la machine virtuelle sur laquelle le script de test sera exécuté pour valider vos solutions :

  1. système d'exploitation Debian 13, avec un noyau Linux version 6.12.57+deb13-amd64,
  2. architecture x86_64 : votre binaire devra donc utiliser un header et un jeu d'instructions x86 32 bits ou 64 bits,
  3. podman version 5.4.2 est installé,
  4. la machine virtuelle n'a pas accès à internet,
  5. elle est configurée avec avec 4 vCPU et 8Go de RAM.

🥷🏼 Bonus

De notre côté, nous avons déjà développé une solution plutôt efficace. Serez-vous capable de battre Synacktiv ?
Pour éviter de vous donner tout de suite le résultat de cette solution, voici le hash d'un fichier texte qui révèle notre score : c4f704d883bdfdd3d4963bb74054af9abd3d78c35b85812dc8a5d2e9cc2df060.
Le fichier sera publié dans le write-up de ce challenge.
 
Bonne chance et joyeuses fêtes de fin d'année !