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 5 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