Writeups
  • ℹ️Infos
  • 🗓️2021
    • DvCTF (to join DaVinciCode)
      • Crypto
        • Baby RSA
        • Ressaye
        • Unbreakable encryption
      • Forensics
        • Broken Portrait
        • Russian Dolls
        • Sus USB
      • Misc
        • Jus de Flag
        • Welcome
      • OSINT
        • Absolutely Nuts
      • Progra
        • Snoop's Mission
      • Reverse
        • Baby Reverse
        • Basic Cracking
        • Code Pyn
      • Steganography
        • Colorful Code
        • Hurt Your Eyes
        • Orchestra of Flags
        • Tyger
      • Web
        • Have I Been Pwned
        • What's Up ?
  • 🗓️2022
    • DvCTF 2022
      • Crypto
        • Cwryptographic Owacle
        • Secure Or Not Secure
        • small weiner
      • Forensics
        • Very Bad Script
      • Misc
        • Data Leak
        • Going postal
        • The Hacker Man
      • OSINT
        • Elon Musk
        • Monkeey
        • Painting Spot
        • Welcome to the DaVinciCTF!
      • Pentest
        • DaVinci's Playlist : Part 1
        • DaVinci's Playlist : Part 2.5
        • DaVinci's Playlist : Part 2
      • Programming
        • Heaven's Gate
        • Sudoku
        • TicTacToe
      • Reverse
        • CryptoVault
        • Mine Game
        • Obfuscated
        • Peripheral Interface Controller XVI
      • Steganography
        • ICMP
        • The Arts of Details
        • Treasure
      • Warmup
        • EBG13
        • FrenchFlag
        • MP3
        • QmFzZTY0
        • RSA
        • Welcome
      • Web
        • CyberStreak v1.0
        • 🎵
    • picoCTF 2022
      • Challs WU
    • @HackDay - Qualifications
      • Crypto
        • Francis Bacon
        • Francs Maçons
        • Rotate-me!
        • Un message codé ?
      • Forensics
        • bad_timing_for_reversing
      • Hardware
        • Cubik'cipher
        • WebSDR
      • Reverse
        • Calling Conventions
        • Memory Investigation
      • Steganography
        • I can make pictures
        • J'ai perdu le flag :(
        • Pokémons
        • Un coup de maître
        • Un logo cachotier
      • Web
        • GIT!
        • Part. 1 - Uploads
        • Part. 2 - Old md5
        • Part. 3 - sudo python
    • 404CTF
      • Crypto
        • Un simple oracle [1/2]
        • Un simple oracle [2/2]
      • Misc
        • Je suis une théière
        • Pierre-papier-Hallebarde
        • GoGOLFplex
      • OSINT
        • À l'aube d'un échange
        • Collaborateur suspect
        • Equipement désuet
      • Reverse
        • Mot de passe ?
      • Steganography
        • La plume à la main
        • PNG : Un logo obèse [1/4]
        • PNG : Drôles de chimères [2/4]
        • Toujours obèse [3/4]
      • Web
        • Fiché JS
        • Le braquage
        • Du gâteau
        • En construction !
    • Operation Kernel
      • Crypto
        • Scytale
      • Forensics
        • Research Paper
        • Excel Confidential
      • Reverse
        • CryptoLocker
        • What_If_CryptoLocker
      • Social Engineering
        • Pour vivre secure vivons caché
        • Pour vivre secure vivons caché Part 2
      • Stegano
        • AudioSpectre
        • Datacenter
        • Takazume
      • WEB
        • Research paper blog
        • SQL Project 1
        • SQL Project 2
        • SQL Project 3
        • Tenue de soirée requise
  • 🗓️2023
    • 404CTF 2023
      • Résultats
      • Analyse forensique
        • Pêche au livre
        • Le Mystère du roman d'amour
        • Les Mystères du cluster de la Comtesse de Ségur [1/2]
        • Lettres volatiles
        • Note de bas de page
      • Cloud
        • Le Sot
        • Le Cluster de Madame Bovary
        • Harpagon et le magot
        • Les nuages menaçants 1/3
        • Les nuages menaçants 2/3
      • Cryptanalyse
        • Recette
        • ASCON Marchombre
      • Divers
        • Bienvenue
        • Exemple de connexion à distance
        • Discord
        • À vos plumes !
      • Exploitation de binaires
        • Je veux la lune !
      • Programmation
        • L'Inondation
        • Des mots, des mots, des mots
      • Radio-Fréquence
        • Navi
        • Avez-vous vu les cascades du hérisson ?
        • Le Plombier du câble
        • Ballistic Missile Submarine
      • Renseignement en sources ouvertes
        • Le Tour de France
        • Les OSINTables [1/3]
        • Un vol ?
        • L'âme d'un poète et le coeur d'une femme [1/4]
        • L'âme d'un poète et le coeur d'une femme [2/4]
        • L'âme d'un poète et le coeur d'une femme [3/4]
        • L'âme d'un poète et le coeur d'une femme [4/4]
      • Rétro Ingénierie
        • Le Divin Crackme
        • L'Inspiration en images
      • Sécurité Matérielle
        • Un courrier suspect
        • Un réveil difficile
      • Stéganographie
        • Odobenus Rosmarus
        • L'Œuvre
        • Les Félicitations
        • En Profondeur
        • Le Rouge et le vert, avec un soupçon de bleu
      • Web
        • Le Loup et le renard
        • L'Académie du détail
        • La Vie Française
        • Fuite en 1791
        • L'Épistolaire moderne
        • Chanson d'Inde
      • Web3
        • Art
        • L'Antiquaire, tête en l'air
  • 🗓️2025
    • 404CTF 2025
      • Résultats
      • Algorithmique Quantique
        • Machinerie quantique
        • Grover (1/2)
        • Grover (2/2)
      • Analyse forensique
        • USB 51
        • Forensic et Mat [1/2]
        • Apprenti Carver [1/2]
        • Dockerflag
        • Forensic et Mat [2/2]
        • Apprenti Carver [2/2]
        • Tape ton MDP
        • Toortik Triflexation [1/2]
        • Toortik Triflexation [2/2]
      • Cryptanalyse
        • Message lointain
        • Entretien galactique
        • R1R2
        • Saturn Cipher
        • Planètes anormales
        • Spacemark
        • You spin me round
        • Dérive dans l'espace
        • Lunette cosmico galactique
        • Jupiter Cipher
        • Courbe de Lancement
        • More Space
      • Divers
        • Bienvenue
        • Pix2Num
        • Politique de confidentialité
        • Space Traveller
        • Satellisation
        • Étoile binaire [2/2]
      • Exploitation de binaires
        • Gorfou en danger [1/3]
        • Gorfou en danger [2/3]
        • Gorfou en danger [3/3]
        • Spaaaaaaace
        • KerberINT Space Program
        • 22 bytes pour sauver l'univers
        • Bounty Machine
        • Space Odyssey
        • Solaris
        • Cosmic-Base
        • Kalaxy
        • Bokit
      • Intelligence Artificielle
        • Gorfoustral (1/3)
        • Gorfoustral (2/3)
        • Gorfoustral (3/3)
        • Du tatouage
      • Renseignement en sources ouvertes
        • L'addition est salée
        • Une mission inconnue
        • Earth Attack (0/2)
        • Un satellite qui vaut de l'or
        • Un peu de vacances
        • La ville en rose
        • Earth Attack (1/2)
        • La tête dans les étoiles
        • En eaux sombres
        • Earth Attack (2/2)
        • DPOsint
      • Réaliste
        • The LDAP Chronicles
        • Houston, we have a problem
        • Named Resolve
        • The AD Guardians
        • The GPO Mission
        • Ghost Membership
      • Rétro-Ingénierie
        • Cbizarre [1/2]
        • Cbizarre [2/2]
        • Reversconstrictor
        • 3x3cut3_m3
        • Inscription
        • Étoile binaire [1/2]
        • RFC 9452 bis: IP over Rockets with Quality of Service
      • Sécurité matérielle
        • Trop d'IQ
        • Space Radio
        • R16D4
        • Comment est votre température ?
        • Code Radiospatial n°1
        • Unidentified Serial Bus [1/2]
        • Unidentified Serial Bus [2/2]
      • Sécurité Web
        • Cheese Chess
        • Rainbow Rocket
        • Fire Server
        • Sideral Noise
        • Space Fleet Command
Powered by GitBook
On this page

Was this helpful?

  1. 2025
  2. 404CTF 2025
  3. Divers

Space Traveller

PreviousPolitique de confidentialitéNextSatellisation

Last updated 22 days ago

Was this helpful?

Catégorie: Divers - Difficulté: Facile

Description:

Solution:

Voici le script afin de terminer le jeu (infernal) :




import json
import time
import random

sio = socketio.Client(logger=False, engineio_logger=False)
score = 0
flag = None
game_started_successfully = False
target_score = 90

waves_pending_completion = [] 

PLAYER_X_POS = 50
PLAYER_WIDTH = 49
PLAYER_HEIGHT = 40 
ASTEROID_WIDTH = 55
ASTEROID_HEIGHT = 48
ASTEROID_START_X = 400
TARGET_ASTEROID_X_FOR_COMPLETION = -2.0 
GAME_TICK_INTERVAL_SECONDS = 0.030 
MAX_PLAYER_Y = 600 # Hauteur du canvas du jeu
EFFECTIVE_MAX_PLAYER_Y = MAX_PLAYER_Y - PLAYER_HEIGHT # Max Y pour le coin supérieur gauche du joueur

@sio.event
def connect():
    print("Connecté au serveur !")
    print("Envoi de game_start...")
    sio.emit("message", "game_start")
    global game_started_successfully
    game_started_successfully = True

@sio.event
def connect_error(data):
    print(f"Erreur de connexion : {data}")

def is_player_safe(player_y, spawns_for_wave):
    player_top = player_y
    player_bottom = player_y + PLAYER_HEIGHT
    for asteroid in spawns_for_wave:
        asteroid_top = asteroid['y']
        asteroid_bottom = asteroid['y'] + ASTEROID_HEIGHT
        # Condition de collision (chevauchement vertical)
        # Le joueur est touché si le bas du joueur est sous le haut de l'astéroïde ET
        # le haut du joueur est au-dessus du bas de l'astéroïde.
        # C'est-à-dire : player_bottom > asteroid_top AND player_top < asteroid_bottom
        if player_bottom > asteroid_top and player_top < asteroid_bottom:
            return False # Collision
    return True # Pas de collision

@sio.on("message")
def on_server_message(type_interne, data_interne=None):
    global score, flag, waves_pending_completion

    if type_interne == "message":
        if data_interne is None:
            print("Erreur: type_interne est 'message' mais data_interne est None.")
            return
        try:
            event_data = json.loads(data_interne)
            actual_event_type = event_data.get("event")

            if actual_event_type == "score_up":
                score += 1
                print(f"Score augmenté ! Score actuel : {score}")
                if score >= target_score and not flag:
                    print(f"Score de {target_score} atteint ou dépassé ! En attente du flag.")
            
            elif actual_event_type == "reward":
                flag = event_data.get("flag")
                print(f"Récompense reçue ! Flag : {flag}")
                if flag: 
                    sio.disconnect()

            elif actual_event_type == "new_wave":
                wave_index = event_data.get("i")
                spawns = event_data.get("spawns", [])
                
                chosen_player_y_for_wave = EFFECTIVE_MAX_PLAYER_Y // 2 # Default
                if spawns:
                    speed = spawns[0].get("speed", 5) 
                    distance_to_travel = ASTEROID_START_X - TARGET_ASTEROID_X_FOR_COMPLETION
                    ticks_needed = distance_to_travel / speed
                    base_delay_seconds = ticks_needed * GAME_TICK_INTERVAL_SECONDS
                    
                    found_safe_y = False
                    # Essayer de trouver une position Y sûre par incréments
                    for y_candidate in range(0, EFFECTIVE_MAX_PLAYER_Y + 1, 5):
                        if is_player_safe(y_candidate, spawns):
                            chosen_player_y_for_wave = y_candidate
                            found_safe_y = True
                            break
                    if not found_safe_y:
                        # Si aucune position sûre n'est trouvée par incréments, essayer des positions aléatoires
                        for _ in range(10): # 10 tentatives aléatoires
                            y_candidate = random.randint(0, EFFECTIVE_MAX_PLAYER_Y)
                            if is_player_safe(y_candidate, spawns):
                                chosen_player_y_for_wave = y_candidate
                                found_safe_y = True
                                break
                    if not found_safe_y:
                         print(f"AVERTISSEMENT: Aucune position Y sûre trouvée pour la vague {wave_index}. Utilisation de {chosen_player_y_for_wave}.")

                    actual_completion_delay = base_delay_seconds + random.uniform(0.05, 0.15) # Petit délai aléatoire
                    completion_time = time.time() + actual_completion_delay
                    
                    waves_pending_completion.append({
                        "index": wave_index, 
                        "completion_time": completion_time, 
                        "playerY_at_completion": chosen_player_y_for_wave
                    })
                    print(f"Nouvelle vague {wave_index} (speed {speed:.2f}). Player_Y choisi: {chosen_player_y_for_wave}. Complétion en ~{actual_completion_delay:.2f}s.")
                else: # Pas de spawns, vague vide ?
                    print(f"Nouvelle vague {wave_index} sans données de spawn. Utilisation d'un délai par défaut et Y par défaut.")
                    completion_time = time.time() + random.uniform(1.0, 1.5)
                    waves_pending_completion.append({"index": wave_index, "completion_time": completion_time, "playerY_at_completion": chosen_player_y_for_wave})

            elif actual_event_type == "game_over":
                print("Game Over reçu du serveur.")
                if not flag:
                    sio.disconnect()

        except json.JSONDecodeError:
            print(f"Erreur de décodage JSON pour les données : {data_interne}")
        except Exception as e:
            print(f"Erreur lors du traitement du message (type interne 'message') du serveur : {e}, données: {data_interne}")
    
    elif type_interne == "ping":
        sio.emit("message", "pong") 

@sio.event
def disconnect():
    print("Déconnecté du serveur.")

def process_pending_waves():
    global waves_pending_completion
    if not sio.connected or not game_started_successfully or flag:
        return

    current_time = time.time()
    waves_pending_completion.sort(key=lambda w: w["completion_time"]) 
    
    processed_indices_this_tick = set()
    temp_pending_waves = []

    for wave_info in waves_pending_completion:
        if current_time >= wave_info["completion_time"] and wave_info["index"] not in processed_indices_this_tick:
            payload = {"playerY": wave_info["playerY_at_completion"], "waveIndex": wave_info["index"]}
            print(f"Envoi de wave_completed pour l'index: {wave_info['index']} avec playerY: {wave_info['playerY_at_completion']}")
            sio.emit("message", ("wave_completed", json.dumps(payload)))
            processed_indices_this_tick.add(wave_info["index"])
        else:
            temp_pending_waves.append(wave_info)
    waves_pending_completion = temp_pending_waves

# Programme principal
if __name__ == '__main__':
    try:
        print("Tentative de connexion à wss://space-traveler.404ctf.fr/")
        sio.connect('wss://space-traveler.404ctf.fr/', transports=['websocket'], socketio_path='socket.io')
        
        start_time = time.time()
        timeout_seconds = 300 # 5 minutes

        while not game_started_successfully and (time.time() - start_time) < 5:
            time.sleep(0.1)
        
        if not game_started_successfully:
            print("Échec du démarrage du jeu.")
        else:
            print("Jeu démarré. Boucle principale.")
            while sio.connected and not flag and (time.time() - start_time) < timeout_seconds:
                process_pending_waves()
                time.sleep(GAME_TICK_INTERVAL_SECONDS / 2) 
                
                if score >= target_score and not flag:
                    print(f"Score de {score} atteint. Attente du flag...")

        if not flag and (time.time() - start_time) >= timeout_seconds:
            print(f"Timeout: Flag non obtenu après {timeout_seconds} secondes.")
        elif not sio.connected and not flag and not ((time.time() - start_time) >= timeout_seconds) :
             print(f"Déconnecté avant d'obtenir le flag (et avant timeout). Score: {score}")
        elif not flag:
            print(f"Flag non obtenu après la boucle principale (score: {score}).")

    except socketio.exceptions.ConnectionError as e:
        print(f"Erreur de connexion Socket.IO : {e}")
    except Exception as e:
        print(f"Une erreur inattendue est survenue : {e}")
    finally:
        if sio.connected:
            sio.disconnect()
        print(f"Script terminé. Score final: {score}, Flag: {flag if flag else 'Non obtenu'}")

Ce script a été créé en étudiant toutes les fonctions JS qui tournent en arrière plan de l'application web. Lorsque nous lançons ce script, voici ce que nous obtenons (étape par étape) :

[...]

🚩FLAG

404CTF{TR1CH3R_C'4ST_B13N_1_B0N_G4M3_D3S1GN_C'4ST_M13UX}

🗓️
346B
template.py