Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- GPNCTF 2025 Solution: Crypto - restricted oracle
- Hubert „hubertf“ Feyrer, 2025-06-23
- Note:
- - Ugly code generated by many iterations in Google Gemini
- - Restriction of the oracle is to focus on printable chars and padding instand of all 256 possible Byte values as a speedup optimization. Without that, the Limited VM lifetime of 29 minutes would have been too short to finish the full decryption.
- - Further optimization not done here: sort letters by their frequency in German language
- Solution:
- (venv-restricted-oracle) % cat 4gemini.py
- from pwn import *
- from hashlib import sha512
- import sys
- import string
- import os
- import time # For time measurement
- # --- Configuration ---
- # Read hostname from file
- try:
- with open("hostname.oracle", "r") as f:
- HOST = f.read().strip()
- log.info(f"Hostname read from hostname.oracle: {HOST}")
- except FileNotFoundError:
- log.error("Error: hostname.oracle not found. Please create this file with the hostname inside.")
- sys.exit(1)
- except Exception as e:
- log.error(f"Error reading hostname from file: {e}")
- sys.exit(1)
- PORT = 443
- BLOCK_SIZE = 16
- # Define valid ASCII letter byte values for optimization
- # A-Z (65-90), a-z (97-122)
- ASCII_LETTER_BYTES = list(range(ord('A'), ord('Z') + 1)) + list(range(ord('a'), ord('z') + 1))
- # --- Global Variables ---
- # iv and initial_cipher_blocks are set once at connection start
- initial_iv = None
- initial_cipher_blocks = []
- # Query counter for debugging
- QUERY_COUNT = 0
- # --- Helper Functions ---
- def xor_bytes(a: bytes, b: bytes) -> bytes:
- """XOR two byte strings."""
- return bytes(x ^ y for x, y in zip(a, b))
- def oracle_query(io, ciphertext: bytes) -> bool:
- """Sends a ciphertext to the oracle and returns True if padding is valid."""
- global QUERY_COUNT
- QUERY_COUNT += 1
- io.sendlineafter(b"speak to the oracle: ", ciphertext.hex().encode())
- response = io.recvline().strip()
- if b"Oracle says: True" in response:
- return True
- elif b"Oracle says: False" in response:
- return False
- else:
- log.error(f"Unexpected oracle response: '{response.decode(errors='ignore')}'. Full line: {response}")
- raise EOFError("Unexpected oracle response, likely connection closed by server.")
- def decrypt_block(io, cipher_block: bytes, prev_block: bytes, block_index_total: int, total_blocks: int) -> bytes:
- """
- Decrypts a single AES-CBC block using the padding oracle.
- block_index_total: The 0-based index of the block in the total ciphertext (0 to total_blocks-1).
- """
- start_time = time.time() # Start time measurement
- # Adjusted block numbering: 1/44, 2/44, ... (from the beginning, as this makes more sense for display)
- # block_num_display is the 1-based block, counted from the START of the ciphertext
- block_num_display = block_index_total + 1
- log.info(f"[+] Processing Block {block_num_display}/{total_blocks}...")
- intermediate_bytes = bytearray(BLOCK_SIZE)
- # Iterate from the last byte (index 15) to the first byte (index 0)
- for p_idx in range(BLOCK_SIZE - 1, -1, -1):
- # Desired padding value for the current byte position
- # This is the value that needs to be formed by (intermediate_bytes[p_idx] XOR guess_byte)
- # combined with previous intermediate bytes to make the oracle return True.
- target_padding_value = BLOCK_SIZE - p_idx
- log.debug(f"Targeting byte index {p_idx} (from start), aiming for padding {target_padding_value}")
- # Construct the prefix of the modified previous block (C'_i-1)
- # These bytes are chosen such that when XORed with their respective intermediate_bytes,
- # they result in 'target_padding_value'.
- modified_prev_block_prefix = bytearray(BLOCK_SIZE)
- for j in range(p_idx + 1, BLOCK_SIZE):
- modified_prev_block_prefix[j] = intermediate_bytes[j] ^ target_padding_value
- found_byte = False
- # --- OPTIMIZATION START (RE-REVISED) ---
- candidate_guess_bytes = set() # Use a set to avoid duplicates
- # Case 1: Plaintext byte is an ASCII letter
- # P[p_idx] = (guess_byte XOR target_padding_value) XOR prev_block[p_idx]
- # -> guess_byte = (P[p_idx] XOR prev_block[p_idx]) XOR target_padding_value
- for ascii_char_code in ASCII_LETTER_BYTES:
- required_intermediate_byte = ascii_char_code ^ prev_block[p_idx]
- candidate_guess_byte = required_intermediate_byte ^ target_padding_value
- candidate_guess_bytes.add(candidate_guess_byte)
- # Case 2: Plaintext byte is ANY possible PKCS7 padding byte value (1 to BLOCK_SIZE)
- # This is crucial for the last block, where the actual padding value could be
- # different from the 'target_padding_value' we are currently forcing.
- # For each possible actual padding value 'k', we calculate the 'guess_byte'
- # that would make P[p_idx] = k IF the oracle considers it valid padding.
- for possible_actual_padding_value in range(1, BLOCK_SIZE + 1):
- required_intermediate_byte = possible_actual_padding_value ^ prev_block[p_idx]
- candidate_guess_byte = required_intermediate_byte ^ target_padding_value
- candidate_guess_bytes.add(candidate_guess_byte)
- guesses_to_try_optimized = sorted(list(candidate_guess_bytes)) # Sort for deterministic behavior
- # First, try the optimized guesses
- for guess_byte in guesses_to_try_optimized:
- # Create the full modified previous block for the current guess
- modified_prev_block = bytearray(modified_prev_block_prefix)
- modified_prev_block[p_idx] = guess_byte
- # Send the crafted ciphertext to the oracle
- crafted_ciphertext = bytes(modified_prev_block) + cipher_block
- try:
- if oracle_query(io, crafted_ciphertext):
- intermediate_bytes[p_idx] = guess_byte ^ target_padding_value
- log.debug(f" Found intermediate byte {p_idx}: {hex(intermediate_bytes[p_idx])} (optimized)")
- found_byte = True
- break
- except EOFError:
- raise # Re-raise EOFError to allow main loop to handle reconnection
- except Exception as e:
- log.warning(f"Error during optimized guess query: {e}. Skipping this guess.")
- # If not found with optimized guesses, fall back to all 256 bytes
- if not found_byte:
- log.warning(f"Optimized guess failed for byte index {p_idx}. Falling back to all 256 possibilities. This may exceed MAX_TRIES.")
- for guess_byte in range(256):
- # Skip if this guess was already tried in the optimized set
- if guess_byte in guesses_to_try_optimized:
- continue
- modified_prev_block = bytearray(modified_prev_block_prefix)
- modified_prev_block[p_idx] = guess_byte
- crafted_ciphertext = bytes(modified_prev_block) + cipher_block
- try:
- if oracle_query(io, crafted_ciphertext):
- intermediate_bytes[p_idx] = guess_byte ^ target_padding_value
- log.debug(f" Found intermediate byte {p_idx}: {hex(intermediate_bytes[p_idx])} (fallback)")
- found_byte = True
- break
- except EOFError:
- raise # Re-raise EOFError
- except Exception as e:
- log.warning(f"Error during fallback guess query: {e}. Skipping this guess.")
- # --- OPTIMIZATION END ---
- if not found_byte:
- log.error(f"Failed to find byte at index {p_idx} even with ALL 256 guesses. This indicates a critical problem.")
- raise EOFError("Byte decryption failed, forcing reconnection.") # Use EOFError for consistent reconnection logic
- # Calculate the actual plaintext block P_i = I ^ C_{i-1}
- decrypted_block_bytes = xor_bytes(bytes(intermediate_bytes), prev_block)
- end_time = time.time() # End time measurement
- block_time = end_time - start_time
- log.success(f"[✓] Block {block_num_display} decrypted: '{decrypted_block_bytes.decode(errors='ignore').strip()}'")
- log.info(f"[i] Block time: {block_time:.2f}s")
- return bytes(decrypted_block_bytes)
- def connect_and_get_initial_data():
- """Connects to the server, gets initial data, and sets global variables."""
- global initial_iv, initial_cipher_blocks, QUERY_COUNT
- QUERY_COUNT = 0 # Reset query counter for new connection
- log.info("Connecting to the Pad Server...")
- io = remote(HOST, PORT, ssl=True)
- log.info("Connected.")
- welcome_message_expected = b"Welcome to the Pad Server!"
- try:
- received_data = io.recvuntil(b"\n", timeout=5)
- if welcome_message_expected not in received_data:
- received_data += io.recv(timeout=1)
- if welcome_message_expected not in received_data:
- log.error(f"Did not receive expected welcome message. Received: '{received_data.decode(errors='ignore')}'")
- raise EOFError("Did not receive welcome message, connection likely closed by server.")
- else:
- log.info(f"Received greeting: {received_data.decode(errors='ignore').strip()}")
- except EOFError:
- log.error("Connection closed by server while waiting for welcome message.")
- raise
- except Exception as e:
- log.error(f"Error receiving welcome message: {e}")
- raise EOFError(f"Error during welcome message reception: {e}")
- xor_flag_hex = io.recvline().strip().decode()
- xor_flag_bytes = bytes.fromhex(xor_flag_hex) # Convert to bytes immediately
- log.info(f"XOR flag length: {len(xor_flag_bytes) * 8}") # Display length in bits
- chall_ciphertext_hex = io.recvline().strip().decode()
- chall_ciphertext_bytes = bytes.fromhex(chall_ciphertext_hex) # Convert to bytes immediately
- log.info(f"Challenge length: {len(chall_ciphertext_bytes) * 8}") # Display length in bits
- initial_iv = chall_ciphertext_bytes[:BLOCK_SIZE]
- initial_cipher_blocks = [chall_ciphertext_bytes[i:i + BLOCK_SIZE] for i in range(BLOCK_SIZE, len(chall_ciphertext_bytes), BLOCK_SIZE)]
- return io, xor_flag_bytes
- def print_partial_decrypted_text(decrypted_blocks: list):
- """Prints the currently accumulated decrypted text."""
- if not decrypted_blocks:
- log.info("No blocks decrypted yet.")
- return
- partial_text_bytes = b"".join(decrypted_blocks)
- # Attempt to remove padding for display, but be robust to incomplete blocks
- try:
- pad_byte = partial_text_bytes[-1]
- if 1 <= pad_byte <= BLOCK_SIZE and len(partial_text_bytes) >= pad_byte and all(b == pad_byte for b in partial_text_bytes[-pad_byte:]):
- display_text = partial_text_bytes[:-pad_byte].decode(errors='ignore')
- else:
- display_text = partial_text_bytes.decode(errors='ignore')
- except (IndexError, UnicodeDecodeError):
- display_text = partial_text_bytes.decode(errors='ignore') # Fallback to raw if unpadding/decoding fails for partial text
- log.info(f"\n--- Partial decrypted text so far ({len(decrypted_blocks)} blocks) ---\n{display_text}\n----------------------------------")
- # --- Main Program ---
- if __name__ == "__main__":
- # Progress loading/saving removed as requested.
- full_decrypted_chall_blocks_storage = []
- current_io = None
- xor_flag_bytes = None
- while True:
- try:
- current_io, xor_flag_bytes = connect_and_get_initial_data()
- total_blocks = len(initial_cipher_blocks)
- log.info(f"Total ciphertext blocks (excluding IV): {total_blocks}")
- # Always start from the last block, as there is no saved progress
- start_i = total_blocks - 1
- log.info(f"Starting decryption from block {start_i + 1}/{total_blocks} (from start).")
- for i in range(start_i, -1, -1):
- current_cipher_block = initial_cipher_blocks[i]
- previous_block_for_xor = initial_cipher_blocks[i-1] if i > 0 else initial_iv
- decrypted_current_block = decrypt_block(current_io, current_cipher_block, previous_block_for_xor, i, total_blocks)
- # Insert the decrypted block at the beginning of the list
- # so full_decrypted_chall_blocks_storage ends up with blocks in correct order
- # (Block 0, Block 1, ...)
- full_decrypted_chall_blocks_storage.insert(0, decrypted_current_block)
- log.info("All blocks successfully decrypted in this session.")
- break
- except EOFError as e:
- log.warning(f"Connection lost (likely MAX_TRIES reached or server closed connection): {e}. Reconnecting and trying again.")
- print_partial_decrypted_text(full_decrypted_chall_blocks_storage) # Print partial text on EOFError
- if current_io:
- current_io.close()
- # Clear the decrypted blocks if connection lost, as the source text might have changed.
- # This ensures a fresh start for decryption with the new challenge.
- full_decrypted_chall_blocks_storage = []
- except Exception as e:
- log.error(f"An unexpected error occurred: {e}")
- print_partial_decrypted_text(full_decrypted_chall_blocks_storage) # Print partial text on any other Exception
- if current_io:
- current_io.close()
- sys.exit(1) # Exit after unexpected error
- finally:
- if current_io and current_io.connected:
- current_io.close()
- # --- Flag Recovery (executed only if all blocks have been decrypted) ---
- log.info("All blocks decrypted. Proceeding to flag recovery.")
- full_decrypted_chall_padded = b"".join(full_decrypted_chall_blocks_storage)
- # Remove PKCS7 padding
- decrypted_chall = b""
- try:
- pad_byte = full_decrypted_chall_padded[-1]
- if 1 <= pad_byte <= BLOCK_SIZE and all(b == pad_byte for b in full_decrypted_chall_padded[-pad_byte:]):
- decrypted_chall = full_decrypted_chall_padded[:-pad_byte]
- log.success(f"Decrypted Challenge (unpadded): {decrypted_chall.decode(errors='ignore')}")
- else:
- log.warning("Padding looks incorrect or no padding found. Using full decrypted data.")
- decrypted_chall = full_decrypted_chall_padded
- except IndexError:
- log.warning("Decrypted challenge is empty. Check logic or if all blocks were retrieved.")
- decrypted_chall = full_decrypted_chall_padded
- final_output_text = decrypted_chall.decode(errors='ignore')
- log.info(f"\n--- Final decrypted text (raw) ---\n{final_output_text}\n----------------------------------")
- found_flag_output = False
- # Try removing 0-4 appended random characters
- for appended_chars_count in range(5):
- if len(decrypted_chall) - appended_chars_count >= 0:
- potential_text_with_prefix = decrypted_chall[:-appended_chars_count]
- if len(potential_text_with_prefix) >= 3:
- text_minus_3 = potential_text_with_prefix[:-3]
- if text_minus_3:
- sha512_digest = sha512(text_minus_3).digest()
- if len(sha512_digest) == len(xor_flag_bytes):
- potential_flag = xor_bytes(sha512_digest, xor_flag_bytes)
- # Check for GPNCTF{ prefix (case-insensitive)
- if b"gpnctf{" in potential_flag.lower():
- log.success(f"Potential Flag found (after removing {appended_chars_count} appended chars): {potential_flag.decode(errors='ignore')}")
- found_flag_output = True
- else:
- log.warning(f"Length mismatch: SHA512 digest ({len(sha512_digest)}) vs XORed Flag ({len(xor_flag_bytes)}). Skipping.")
- else:
- log.debug(f"Potential text (after removing {appended_chars_count} chars and then[:-3]) is too short for hash. Result: '{potential_text_with_prefix.decode(errors='ignore')}'")
- else:
- log.debug(f"Potential text (after removing {appended_chars_count} chars) is too short for [:-3] slice. Result: '{potential_text_with_prefix.decode(errors='ignore')}'")
- if not found_flag_output:
- log.failure("No flag with prefix GPNCTF{ found. Decrypted challenge might be incomplete or logic flawed.")
- log.info("Script finished.")
- (venv-restricted-oracle) % python3 4gemini.py | & tee 4gemini.out1
- [*] Hostname read from hostname.oracle: goldenhaven-of-preposterous-cash.gpn23.ctf.kitctf.de
- [*] Connecting to the Pad Server...
- [x] Opening connection to goldenhaven-of-preposterous-cash.gpn23.ctf.kitctf.de on port 443
- [x] Opening connection to goldenhaven-of-preposterous-cash.gpn23.ctf.kitctf.de on port 443: Trying 37.228.169.4
- [+] Opening connection to goldenhaven-of-preposterous-cash.gpn23.ctf.kitctf.de on port 443: Done
- [*] Connected.
- [*] Received greeting: Welcome to the Pad Server!
- [*] XOR flag length: 512
- [*] Challenge length: 6656
- [*] Total ciphertext blocks (excluding IV): 51
- [*] Starting decryption from block 51/51 (from start).
- [*] [+] Processing Block 51/51...
- [+] [✓] Block 51 decrypted: 'BOE'
- [*] [i] Block time: 11.83s
- [*] [+] Processing Block 50/51...
- [+] [✓] Block 50 decrypted: 'reDingezuhaltenE'
- [*] [i] Block time: 11.01s
- [*] [+] Processing Block 49/51...
- [+] [✓] Block 49 decrypted: 'eziehungenfrstar'
- [*] [i] Block time: 14.06s
- [*] [+] Processing Block 48/51...
- [+] [✓] Block 48 decrypted: 'rwieabsurdesistB'
- [*] [i] Block time: 12.90s
- [*] [+] Processing Block 47/51...
- [+] [✓] Block 47 decrypted: 'hesChaosistdarbe'
- [*] [i] Block time: 11.81s
- [*] [+] Processing Block 46/51...
- [+] [✓] Block 46 decrypted: 'ingltigromantisc'
- [*] [i] Block time: 12.03s
- [*] [+] Processing Block 45/51...
- [+] [✓] Block 45 decrypted: 'darberwieallgeme'
- [*] [i] Block time: 12.49s
- [*] [+] Processing Block 44/51...
- [+] [✓] Block 44 decrypted: 'steineGeschichte'
- [*] [i] Block time: 11.84s
- [*] [+] Processing Block 43/51...
- [+] [✓] Block 43 decrypted: 'ielmehralsdasEsi'
- [*] [i] Block time: 13.45s
- [*] [+] Processing Block 42/51...
- [+] [✓] Block 42 decrypted: 'stesWerkistebenv'
- [*] [i] Block time: 10.63s
- [*] [+] Processing Block 41/51...
- [+] [✓] Block 41 decrypted: 'erfhrenHaighsjng'
- [*] [i] Block time: 11.99s
- [*] [+] Processing Block 40/51...
- [+] [✓] Block 40 decrypted: 'einenVerkaufweit'
- [*] [i] Block time: 12.48s
- [*] [+] Processing Block 39/51...
- [+] [✓] Block 39 decrypted: 'sUnternehmenohne'
- [*] [i] Block time: 10.87s
- [*] [+] Processing Block 38/51...
- [+] [✓] Block 38 decrypted: 'leumwandelnundda'
- [*] [i] Block time: 10.68s
- [*] [+] Processing Block 37/51...
- [+] [✓] Block 37 decrypted: 'ssenschaftsantei'
- [*] [i] Block time: 9.47s
- [*] [+] Processing Block 36/51...
- [+] [✓] Block 36 decrypted: 'eiligungeninGeno'
- [*] [i] Block time: 10.04s
- [*] [+] Processing Block 35/51...
- [+] [✓] Block 35 decrypted: 'italgeberihreBet'
- [*] [i] Block time: 9.70s
- [*] [+] Processing Block 34/51...
- [+] [✓] Block 34 decrypted: 'eiauchdassdieKap'
- [*] [i] Block time: 13.12s
- [*] [+] Processing Block 33/51...
- [+] [✓] Block 33 decrypted: 'eschrnktGesprchs'
- [*] [i] Block time: 9.19s
- [*] [+] Processing Block 32/51...
- [+] [✓] Block 32 decrypted: 'tdenRegistrarenb'
- [*] [i] Block time: 8.26s
- [*] [+] Processing Block 31/51...
- [+] [✓] Block 31 decrypted: 'taufdasGeschftmi'
- [*] [i] Block time: 8.87s
- [*] [+] Processing Block 30/51...
- [+] [✓] Block 30 decrypted: 'chSwitchebennich'
- [*] [i] Block time: 11.08s
- [*] [+] Processing Block 29/51...
- [+] [✓] Block 29 decrypted: 'raufderWelthatsi'
- [*] [i] Block time: 10.70s
- [*] [+] Processing Block 28/51...
- [+] [✓] Block 28 decrypted: 'nderenGrosshndle'
- [*] [i] Block time: 11.10s
- [*] [+] Processing Block 27/51...
- [+] [✓] Block 27 decrypted: 'dersalsfastallea'
- [*] [i] Block time: 12.34s
- [*] [+] Processing Block 26/51...
- [+] [✓] Block 26 decrypted: 'eitJahrzehntenan'
- [*] [i] Block time: 9.56s
- [*] [+] Processing Block 25/51...
- [+] [✓] Block 25 decrypted: 'elauchberuflichs'
- [*] [i] Block time: 11.52s
- [*] [+] Processing Block 24/51...
- [+] [✓] Block 24 decrypted: 'zidbegleitetWeib'
- [*] [i] Block time: 8.56s
- [*] [+] Processing Block 23/51...
- [+] [✓] Block 23 decrypted: 'rgirlSchienensui'
- [*] [i] Block time: 12.67s
- [*] [+] Processing Block 22/51...
- [+] [✓] Block 22 decrypted: 'digkommtnochSupe'
- [*] [i] Block time: 11.40s
- [*] [+] Processing Block 21/51...
- [+] [✓] Block 21 decrypted: 'nGlasfasernotwen'
- [*] [i] Block time: 13.75s
- [*] [+] Processing Block 20/51...
- [+] [✓] Block 20 decrypted: 'ktuellerAusbauvo'
- [*] [i] Block time: 12.11s
- [*] [+] Processing Block 19/51...
- [+] [✓] Block 19 decrypted: 'denRestisteinpun'
- [*] [i] Block time: 14.10s
- [*] [+] Processing Block 18/51...
- [+] [✓] Block 18 decrypted: 'enStellenstattfr'
- [*] [i] Block time: 9.05s
- [*] [+] Processing Block 17/51...
- [+] [✓] Block 17 decrypted: 'ndenanvorgegeben'
- [*] [i] Block time: 12.14s
- [*] [+] Processing Block 16/51...
- [+] [✓] Block 16 decrypted: 'dereEventkmpfefi'
- [*] [i] Block time: 10.06s
- [*] [+] Processing Block 15/51...
- [+] [✓] Block 15 decrypted: 'KlubmitBossundan'
- [*] [i] Block time: 13.42s
- [*] [+] Processing Block 14/51...
- [+] [✓] Block 14 decrypted: 'eiltederBerliner'
- [*] [i] Block time: 10.88s
- [*] [+] Processing Block 13/51...
- [+] [✓] Block 13 decrypted: 'ttwochangesetztt'
- [*] [i] Block time: 11.50s
- [*] [+] Processing Block 12/51...
- [+] [✓] Block 12 decrypted: 'OperationistfrMi'
- [*] [i] Block time: 9.81s
- [*] [+] Processing Block 11/51...
- [+] [✓] Block 11 decrypted: 'ineKartezusetzen'
- [*] [i] Block time: 12.57s
- [*] [+] Processing Block 10/51...
- [+] [✓] Block 10 decrypted: 'hlauistallesaufe'
- [*] [i] Block time: 16.02s
- [*] [+] Processing Block 9/51...
- [+] [✓] Block 9 decrypted: 'nichtbesonderssc'
- [*] [i] Block time: 9.98s
- [*] [+] Processing Block 8/51...
- [+] [✓] Block 8 decrypted: 'lebewiesendasses'
- [*] [i] Block time: 11.68s
- [*] [+] Processing Block 7/51...
- [+] [✓] Block 7 decrypted: 'hichtezahlloseMa'
- [*] [i] Block time: 11.48s
- [*] [+] Processing Block 6/51...
- [+] [✓] Block 6 decrypted: 'rsseinhatdieGesc'
- [*] [i] Block time: 10.48s
- [*] [+] Processing Block 5/51...
- [+] [✓] Block 5 decrypted: 'nauchimmerwoande'
- [*] [i] Block time: 10.37s
- [*] [+] Processing Block 4/51...
- [+] [✓] Block 4 decrypted: 'undingutendaskan'
- [*] [i] Block time: 11.94s
- [*] [+] Processing Block 3/51...
- [+] [✓] Block 3 decrypted: 'nteninschlechten'
- [*] [i] Block time: 12.15s
- [*] [+] Processing Block 2/51...
- [+] [✓] Block 2 decrypted: 'chhintrumtinMome'
- [*] [i] Block time: 10.69s
- [*] [+] Processing Block 1/51...
- [+] [✓] Block 1 decrypted: 'womansichinnerli'
- [*] [i] Block time: 10.90s
- [*] All blocks successfully decrypted in this session.
- [*] Closed connection to goldenhaven-of-preposterous-cash.gpn23.ctf.kitctf.de port 443
- [*] All blocks decrypted. Proceeding to flag recovery.
- [+] Decrypted Challenge (unpadded): womansichinnerlichhintrumtinMomenteninschlechtenundingutendaskannauchimmerwoandersseinhatdieGeschichtezahlloseMalebewiesendassesnichtbesondersschlauistallesaufeineKartezusetzenOperationistfrMittwochangesetztteiltederBerlinerKlubmitBossundandereEventkmpfefindenanvorgegebenenStellenstattfrdenRestisteinpunktuellerAusbauvonGlasfasernotwendigkommtnochSupergirlSchienensuizidbegleitetWeibelauchberuflichseitJahrzehntenandersalsfastalleanderenGrosshndleraufderWelthatsichSwitchebennichtaufdasGeschftmitdenRegistrarenbeschrnktGesprchseiauchdassdieKapitalgeberihreBeteiligungeninGenossenschaftsanteileumwandelnunddasUnternehmenohneeinenVerkaufweiterfhrenHaighsjngstesWerkistebenvielmehralsdasEsisteineGeschichtedarberwieallgemeingltigromantischesChaosistdarberwieabsurdesistBeziehungenfrstarreDingezuhaltenEBOE
- [*]
- --- Final decrypted text (raw) ---
- womansichinnerlichhintrumtinMomenteninschlechtenundingutendaskannauchimmerwoandersseinhatdieGeschichtezahlloseMalebewiesendassesnichtbesondersschlauistallesaufeineKartezusetzenOperationistfrMittwochangesetztteiltederBerlinerKlubmitBossundandereEventkmpfefindenanvorgegebenenStellenstattfrdenRestisteinpunktuellerAusbauvonGlasfasernotwendigkommtnochSupergirlSchienensuizidbegleitetWeibelauchberuflichseitJahrzehntenandersalsfastalleanderenGrosshndleraufderWelthatsichSwitchebennichtaufdasGeschftmitdenRegistrarenbeschrnktGesprchseiauchdassdieKapitalgeberihreBeteiligungeninGenossenschaftsanteileumwandelnunddasUnternehmenohneeinenVerkaufweiterfhrenHaighsjngstesWerkistebenvielmehralsdasEsisteineGeschichtedarberwieallgemeingltigromantischesChaosistdarberwieabsurdesistBeziehungenfrstarreDingezuhaltenEBOE
- ----------------------------------
- [+] Potential Flag found (after removing 4 appended chars): GPNCTF{nic3_guEs5in6_p4DD1n6_iS_fUN}
- [*] Script finished.
- 14.424u 3.295s 9:42.46 3.0% 0+0k 0+0io 67pf+0w
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement