Advertisement
Guest User

GPNCTF 2025 Solution: Crypto - restricted oracle

a guest
Jun 22nd, 2025
182
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 24.92 KB | None | 0 0
  1. GPNCTF 2025 Solution: Crypto - restricted oracle
  2. Hubert „hubertf“ Feyrer, 2025-06-23
  3.  
  4. Note:
  5. - Ugly code generated by many iterations in Google Gemini
  6. - 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.
  7. - Further optimization not done here: sort letters by their frequency in German language
  8.  
  9. Solution:
  10.  
  11. (venv-restricted-oracle) % cat 4gemini.py
  12. from pwn import *
  13. from hashlib import sha512
  14. import sys
  15. import string
  16. import os
  17. import time # For time measurement
  18.  
  19. # --- Configuration ---
  20. # Read hostname from file
  21. try:
  22. with open("hostname.oracle", "r") as f:
  23. HOST = f.read().strip()
  24. log.info(f"Hostname read from hostname.oracle: {HOST}")
  25. except FileNotFoundError:
  26. log.error("Error: hostname.oracle not found. Please create this file with the hostname inside.")
  27. sys.exit(1)
  28. except Exception as e:
  29. log.error(f"Error reading hostname from file: {e}")
  30. sys.exit(1)
  31.  
  32. PORT = 443
  33. BLOCK_SIZE = 16
  34.  
  35. # Define valid ASCII letter byte values for optimization
  36. # A-Z (65-90), a-z (97-122)
  37. ASCII_LETTER_BYTES = list(range(ord('A'), ord('Z') + 1)) + list(range(ord('a'), ord('z') + 1))
  38.  
  39. # --- Global Variables ---
  40. # iv and initial_cipher_blocks are set once at connection start
  41. initial_iv = None
  42. initial_cipher_blocks = []
  43.  
  44. # Query counter for debugging
  45. QUERY_COUNT = 0
  46.  
  47. # --- Helper Functions ---
  48. def xor_bytes(a: bytes, b: bytes) -> bytes:
  49. """XOR two byte strings."""
  50. return bytes(x ^ y for x, y in zip(a, b))
  51.  
  52. def oracle_query(io, ciphertext: bytes) -> bool:
  53. """Sends a ciphertext to the oracle and returns True if padding is valid."""
  54. global QUERY_COUNT
  55. QUERY_COUNT += 1
  56.  
  57. io.sendlineafter(b"speak to the oracle: ", ciphertext.hex().encode())
  58.  
  59. response = io.recvline().strip()
  60. if b"Oracle says: True" in response:
  61. return True
  62. elif b"Oracle says: False" in response:
  63. return False
  64. else:
  65. log.error(f"Unexpected oracle response: '{response.decode(errors='ignore')}'. Full line: {response}")
  66. raise EOFError("Unexpected oracle response, likely connection closed by server.")
  67.  
  68.  
  69. def decrypt_block(io, cipher_block: bytes, prev_block: bytes, block_index_total: int, total_blocks: int) -> bytes:
  70. """
  71. Decrypts a single AES-CBC block using the padding oracle.
  72. block_index_total: The 0-based index of the block in the total ciphertext (0 to total_blocks-1).
  73. """
  74. start_time = time.time() # Start time measurement
  75.  
  76. # Adjusted block numbering: 1/44, 2/44, ... (from the beginning, as this makes more sense for display)
  77. # block_num_display is the 1-based block, counted from the START of the ciphertext
  78. block_num_display = block_index_total + 1
  79.  
  80. log.info(f"[+] Processing Block {block_num_display}/{total_blocks}...")
  81.  
  82. intermediate_bytes = bytearray(BLOCK_SIZE)
  83.  
  84. # Iterate from the last byte (index 15) to the first byte (index 0)
  85. for p_idx in range(BLOCK_SIZE - 1, -1, -1):
  86. # Desired padding value for the current byte position
  87. # This is the value that needs to be formed by (intermediate_bytes[p_idx] XOR guess_byte)
  88. # combined with previous intermediate bytes to make the oracle return True.
  89. target_padding_value = BLOCK_SIZE - p_idx
  90. log.debug(f"Targeting byte index {p_idx} (from start), aiming for padding {target_padding_value}")
  91.  
  92. # Construct the prefix of the modified previous block (C'_i-1)
  93. # These bytes are chosen such that when XORed with their respective intermediate_bytes,
  94. # they result in 'target_padding_value'.
  95. modified_prev_block_prefix = bytearray(BLOCK_SIZE)
  96. for j in range(p_idx + 1, BLOCK_SIZE):
  97. modified_prev_block_prefix[j] = intermediate_bytes[j] ^ target_padding_value
  98.  
  99. found_byte = False
  100.  
  101. # --- OPTIMIZATION START (RE-REVISED) ---
  102. candidate_guess_bytes = set() # Use a set to avoid duplicates
  103.  
  104. # Case 1: Plaintext byte is an ASCII letter
  105. # P[p_idx] = (guess_byte XOR target_padding_value) XOR prev_block[p_idx]
  106. # -> guess_byte = (P[p_idx] XOR prev_block[p_idx]) XOR target_padding_value
  107. for ascii_char_code in ASCII_LETTER_BYTES:
  108. required_intermediate_byte = ascii_char_code ^ prev_block[p_idx]
  109. candidate_guess_byte = required_intermediate_byte ^ target_padding_value
  110. candidate_guess_bytes.add(candidate_guess_byte)
  111.  
  112. # Case 2: Plaintext byte is ANY possible PKCS7 padding byte value (1 to BLOCK_SIZE)
  113. # This is crucial for the last block, where the actual padding value could be
  114. # different from the 'target_padding_value' we are currently forcing.
  115. # For each possible actual padding value 'k', we calculate the 'guess_byte'
  116. # that would make P[p_idx] = k IF the oracle considers it valid padding.
  117. for possible_actual_padding_value in range(1, BLOCK_SIZE + 1):
  118. required_intermediate_byte = possible_actual_padding_value ^ prev_block[p_idx]
  119. candidate_guess_byte = required_intermediate_byte ^ target_padding_value
  120. candidate_guess_bytes.add(candidate_guess_byte)
  121.  
  122. guesses_to_try_optimized = sorted(list(candidate_guess_bytes)) # Sort for deterministic behavior
  123.  
  124. # First, try the optimized guesses
  125. for guess_byte in guesses_to_try_optimized:
  126. # Create the full modified previous block for the current guess
  127. modified_prev_block = bytearray(modified_prev_block_prefix)
  128. modified_prev_block[p_idx] = guess_byte
  129.  
  130. # Send the crafted ciphertext to the oracle
  131. crafted_ciphertext = bytes(modified_prev_block) + cipher_block
  132.  
  133. try:
  134. if oracle_query(io, crafted_ciphertext):
  135. intermediate_bytes[p_idx] = guess_byte ^ target_padding_value
  136. log.debug(f" Found intermediate byte {p_idx}: {hex(intermediate_bytes[p_idx])} (optimized)")
  137. found_byte = True
  138. break
  139. except EOFError:
  140. raise # Re-raise EOFError to allow main loop to handle reconnection
  141. except Exception as e:
  142. log.warning(f"Error during optimized guess query: {e}. Skipping this guess.")
  143.  
  144. # If not found with optimized guesses, fall back to all 256 bytes
  145. if not found_byte:
  146. log.warning(f"Optimized guess failed for byte index {p_idx}. Falling back to all 256 possibilities. This may exceed MAX_TRIES.")
  147. for guess_byte in range(256):
  148. # Skip if this guess was already tried in the optimized set
  149. if guess_byte in guesses_to_try_optimized:
  150. continue
  151.  
  152. modified_prev_block = bytearray(modified_prev_block_prefix)
  153. modified_prev_block[p_idx] = guess_byte
  154. crafted_ciphertext = bytes(modified_prev_block) + cipher_block
  155.  
  156. try:
  157. if oracle_query(io, crafted_ciphertext):
  158. intermediate_bytes[p_idx] = guess_byte ^ target_padding_value
  159. log.debug(f" Found intermediate byte {p_idx}: {hex(intermediate_bytes[p_idx])} (fallback)")
  160. found_byte = True
  161. break
  162. except EOFError:
  163. raise # Re-raise EOFError
  164. except Exception as e:
  165. log.warning(f"Error during fallback guess query: {e}. Skipping this guess.")
  166.  
  167. # --- OPTIMIZATION END ---
  168.  
  169. if not found_byte:
  170. log.error(f"Failed to find byte at index {p_idx} even with ALL 256 guesses. This indicates a critical problem.")
  171. raise EOFError("Byte decryption failed, forcing reconnection.") # Use EOFError for consistent reconnection logic
  172.  
  173. # Calculate the actual plaintext block P_i = I ^ C_{i-1}
  174. decrypted_block_bytes = xor_bytes(bytes(intermediate_bytes), prev_block)
  175.  
  176. end_time = time.time() # End time measurement
  177. block_time = end_time - start_time
  178.  
  179. log.success(f"[✓] Block {block_num_display} decrypted: '{decrypted_block_bytes.decode(errors='ignore').strip()}'")
  180. log.info(f"[i] Block time: {block_time:.2f}s")
  181.  
  182. return bytes(decrypted_block_bytes)
  183.  
  184. def connect_and_get_initial_data():
  185. """Connects to the server, gets initial data, and sets global variables."""
  186. global initial_iv, initial_cipher_blocks, QUERY_COUNT
  187. QUERY_COUNT = 0 # Reset query counter for new connection
  188.  
  189. log.info("Connecting to the Pad Server...")
  190. io = remote(HOST, PORT, ssl=True)
  191. log.info("Connected.")
  192.  
  193. welcome_message_expected = b"Welcome to the Pad Server!"
  194. try:
  195. received_data = io.recvuntil(b"\n", timeout=5)
  196. if welcome_message_expected not in received_data:
  197. received_data += io.recv(timeout=1)
  198.  
  199. if welcome_message_expected not in received_data:
  200. log.error(f"Did not receive expected welcome message. Received: '{received_data.decode(errors='ignore')}'")
  201. raise EOFError("Did not receive welcome message, connection likely closed by server.")
  202. else:
  203. log.info(f"Received greeting: {received_data.decode(errors='ignore').strip()}")
  204.  
  205. except EOFError:
  206. log.error("Connection closed by server while waiting for welcome message.")
  207. raise
  208. except Exception as e:
  209. log.error(f"Error receiving welcome message: {e}")
  210. raise EOFError(f"Error during welcome message reception: {e}")
  211.  
  212. xor_flag_hex = io.recvline().strip().decode()
  213. xor_flag_bytes = bytes.fromhex(xor_flag_hex) # Convert to bytes immediately
  214. log.info(f"XOR flag length: {len(xor_flag_bytes) * 8}") # Display length in bits
  215.  
  216. chall_ciphertext_hex = io.recvline().strip().decode()
  217. chall_ciphertext_bytes = bytes.fromhex(chall_ciphertext_hex) # Convert to bytes immediately
  218. log.info(f"Challenge length: {len(chall_ciphertext_bytes) * 8}") # Display length in bits
  219.  
  220. initial_iv = chall_ciphertext_bytes[:BLOCK_SIZE]
  221. initial_cipher_blocks = [chall_ciphertext_bytes[i:i + BLOCK_SIZE] for i in range(BLOCK_SIZE, len(chall_ciphertext_bytes), BLOCK_SIZE)]
  222.  
  223. return io, xor_flag_bytes
  224.  
  225.  
  226. def print_partial_decrypted_text(decrypted_blocks: list):
  227. """Prints the currently accumulated decrypted text."""
  228. if not decrypted_blocks:
  229. log.info("No blocks decrypted yet.")
  230. return
  231.  
  232. partial_text_bytes = b"".join(decrypted_blocks)
  233. # Attempt to remove padding for display, but be robust to incomplete blocks
  234. try:
  235. pad_byte = partial_text_bytes[-1]
  236. 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:]):
  237. display_text = partial_text_bytes[:-pad_byte].decode(errors='ignore')
  238. else:
  239. display_text = partial_text_bytes.decode(errors='ignore')
  240. except (IndexError, UnicodeDecodeError):
  241. display_text = partial_text_bytes.decode(errors='ignore') # Fallback to raw if unpadding/decoding fails for partial text
  242.  
  243. log.info(f"\n--- Partial decrypted text so far ({len(decrypted_blocks)} blocks) ---\n{display_text}\n----------------------------------")
  244.  
  245.  
  246. # --- Main Program ---
  247. if __name__ == "__main__":
  248.  
  249. # Progress loading/saving removed as requested.
  250. full_decrypted_chall_blocks_storage = []
  251.  
  252. current_io = None
  253. xor_flag_bytes = None
  254.  
  255. while True:
  256. try:
  257. current_io, xor_flag_bytes = connect_and_get_initial_data()
  258.  
  259. total_blocks = len(initial_cipher_blocks)
  260. log.info(f"Total ciphertext blocks (excluding IV): {total_blocks}")
  261.  
  262. # Always start from the last block, as there is no saved progress
  263. start_i = total_blocks - 1
  264.  
  265. log.info(f"Starting decryption from block {start_i + 1}/{total_blocks} (from start).")
  266.  
  267. for i in range(start_i, -1, -1):
  268. current_cipher_block = initial_cipher_blocks[i]
  269. previous_block_for_xor = initial_cipher_blocks[i-1] if i > 0 else initial_iv
  270.  
  271. decrypted_current_block = decrypt_block(current_io, current_cipher_block, previous_block_for_xor, i, total_blocks)
  272.  
  273. # Insert the decrypted block at the beginning of the list
  274. # so full_decrypted_chall_blocks_storage ends up with blocks in correct order
  275. # (Block 0, Block 1, ...)
  276. full_decrypted_chall_blocks_storage.insert(0, decrypted_current_block)
  277.  
  278. log.info("All blocks successfully decrypted in this session.")
  279. break
  280.  
  281. except EOFError as e:
  282. log.warning(f"Connection lost (likely MAX_TRIES reached or server closed connection): {e}. Reconnecting and trying again.")
  283. print_partial_decrypted_text(full_decrypted_chall_blocks_storage) # Print partial text on EOFError
  284. if current_io:
  285. current_io.close()
  286. # Clear the decrypted blocks if connection lost, as the source text might have changed.
  287. # This ensures a fresh start for decryption with the new challenge.
  288. full_decrypted_chall_blocks_storage = []
  289. except Exception as e:
  290. log.error(f"An unexpected error occurred: {e}")
  291. print_partial_decrypted_text(full_decrypted_chall_blocks_storage) # Print partial text on any other Exception
  292. if current_io:
  293. current_io.close()
  294. sys.exit(1) # Exit after unexpected error
  295. finally:
  296. if current_io and current_io.connected:
  297. current_io.close()
  298.  
  299. # --- Flag Recovery (executed only if all blocks have been decrypted) ---
  300. log.info("All blocks decrypted. Proceeding to flag recovery.")
  301. full_decrypted_chall_padded = b"".join(full_decrypted_chall_blocks_storage)
  302.  
  303. # Remove PKCS7 padding
  304. decrypted_chall = b""
  305. try:
  306. pad_byte = full_decrypted_chall_padded[-1]
  307. if 1 <= pad_byte <= BLOCK_SIZE and all(b == pad_byte for b in full_decrypted_chall_padded[-pad_byte:]):
  308. decrypted_chall = full_decrypted_chall_padded[:-pad_byte]
  309. log.success(f"Decrypted Challenge (unpadded): {decrypted_chall.decode(errors='ignore')}")
  310. else:
  311. log.warning("Padding looks incorrect or no padding found. Using full decrypted data.")
  312. decrypted_chall = full_decrypted_chall_padded
  313. except IndexError:
  314. log.warning("Decrypted challenge is empty. Check logic or if all blocks were retrieved.")
  315. decrypted_chall = full_decrypted_chall_padded
  316.  
  317. final_output_text = decrypted_chall.decode(errors='ignore')
  318. log.info(f"\n--- Final decrypted text (raw) ---\n{final_output_text}\n----------------------------------")
  319.  
  320. found_flag_output = False
  321.  
  322. # Try removing 0-4 appended random characters
  323. for appended_chars_count in range(5):
  324. if len(decrypted_chall) - appended_chars_count >= 0:
  325. potential_text_with_prefix = decrypted_chall[:-appended_chars_count]
  326.  
  327. if len(potential_text_with_prefix) >= 3:
  328. text_minus_3 = potential_text_with_prefix[:-3]
  329.  
  330. if text_minus_3:
  331. sha512_digest = sha512(text_minus_3).digest()
  332.  
  333. if len(sha512_digest) == len(xor_flag_bytes):
  334. potential_flag = xor_bytes(sha512_digest, xor_flag_bytes)
  335.  
  336. # Check for GPNCTF{ prefix (case-insensitive)
  337. if b"gpnctf{" in potential_flag.lower():
  338. log.success(f"Potential Flag found (after removing {appended_chars_count} appended chars): {potential_flag.decode(errors='ignore')}")
  339. found_flag_output = True
  340. else:
  341. log.warning(f"Length mismatch: SHA512 digest ({len(sha512_digest)}) vs XORed Flag ({len(xor_flag_bytes)}). Skipping.")
  342. else:
  343. 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')}'")
  344. else:
  345. 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')}'")
  346.  
  347. if not found_flag_output:
  348. log.failure("No flag with prefix GPNCTF{ found. Decrypted challenge might be incomplete or logic flawed.")
  349.  
  350. log.info("Script finished.")
  351.  
  352. (venv-restricted-oracle) % python3 4gemini.py | & tee 4gemini.out1
  353. [*] Hostname read from hostname.oracle: goldenhaven-of-preposterous-cash.gpn23.ctf.kitctf.de
  354. [*] Connecting to the Pad Server...
  355. [x] Opening connection to goldenhaven-of-preposterous-cash.gpn23.ctf.kitctf.de on port 443
  356. [x] Opening connection to goldenhaven-of-preposterous-cash.gpn23.ctf.kitctf.de on port 443: Trying 37.228.169.4
  357. [+] Opening connection to goldenhaven-of-preposterous-cash.gpn23.ctf.kitctf.de on port 443: Done
  358. [*] Connected.
  359. [*] Received greeting: Welcome to the Pad Server!
  360. [*] XOR flag length: 512
  361. [*] Challenge length: 6656
  362. [*] Total ciphertext blocks (excluding IV): 51
  363. [*] Starting decryption from block 51/51 (from start).
  364. [*] [+] Processing Block 51/51...
  365. [+] [✓] Block 51 decrypted: 'BOE'
  366. [*] [i] Block time: 11.83s
  367. [*] [+] Processing Block 50/51...
  368. [+] [✓] Block 50 decrypted: 'reDingezuhaltenE'
  369. [*] [i] Block time: 11.01s
  370. [*] [+] Processing Block 49/51...
  371. [+] [✓] Block 49 decrypted: 'eziehungenfrstar'
  372. [*] [i] Block time: 14.06s
  373. [*] [+] Processing Block 48/51...
  374. [+] [✓] Block 48 decrypted: 'rwieabsurdesistB'
  375. [*] [i] Block time: 12.90s
  376. [*] [+] Processing Block 47/51...
  377. [+] [✓] Block 47 decrypted: 'hesChaosistdarbe'
  378. [*] [i] Block time: 11.81s
  379. [*] [+] Processing Block 46/51...
  380. [+] [✓] Block 46 decrypted: 'ingltigromantisc'
  381. [*] [i] Block time: 12.03s
  382. [*] [+] Processing Block 45/51...
  383. [+] [✓] Block 45 decrypted: 'darberwieallgeme'
  384. [*] [i] Block time: 12.49s
  385. [*] [+] Processing Block 44/51...
  386. [+] [✓] Block 44 decrypted: 'steineGeschichte'
  387. [*] [i] Block time: 11.84s
  388. [*] [+] Processing Block 43/51...
  389. [+] [✓] Block 43 decrypted: 'ielmehralsdasEsi'
  390. [*] [i] Block time: 13.45s
  391. [*] [+] Processing Block 42/51...
  392. [+] [✓] Block 42 decrypted: 'stesWerkistebenv'
  393. [*] [i] Block time: 10.63s
  394. [*] [+] Processing Block 41/51...
  395. [+] [✓] Block 41 decrypted: 'erfhrenHaighsjng'
  396. [*] [i] Block time: 11.99s
  397. [*] [+] Processing Block 40/51...
  398. [+] [✓] Block 40 decrypted: 'einenVerkaufweit'
  399. [*] [i] Block time: 12.48s
  400. [*] [+] Processing Block 39/51...
  401. [+] [✓] Block 39 decrypted: 'sUnternehmenohne'
  402. [*] [i] Block time: 10.87s
  403. [*] [+] Processing Block 38/51...
  404. [+] [✓] Block 38 decrypted: 'leumwandelnundda'
  405. [*] [i] Block time: 10.68s
  406. [*] [+] Processing Block 37/51...
  407. [+] [✓] Block 37 decrypted: 'ssenschaftsantei'
  408. [*] [i] Block time: 9.47s
  409. [*] [+] Processing Block 36/51...
  410. [+] [✓] Block 36 decrypted: 'eiligungeninGeno'
  411. [*] [i] Block time: 10.04s
  412. [*] [+] Processing Block 35/51...
  413. [+] [✓] Block 35 decrypted: 'italgeberihreBet'
  414. [*] [i] Block time: 9.70s
  415. [*] [+] Processing Block 34/51...
  416. [+] [✓] Block 34 decrypted: 'eiauchdassdieKap'
  417. [*] [i] Block time: 13.12s
  418. [*] [+] Processing Block 33/51...
  419. [+] [✓] Block 33 decrypted: 'eschrnktGesprchs'
  420. [*] [i] Block time: 9.19s
  421. [*] [+] Processing Block 32/51...
  422. [+] [✓] Block 32 decrypted: 'tdenRegistrarenb'
  423. [*] [i] Block time: 8.26s
  424. [*] [+] Processing Block 31/51...
  425. [+] [✓] Block 31 decrypted: 'taufdasGeschftmi'
  426. [*] [i] Block time: 8.87s
  427. [*] [+] Processing Block 30/51...
  428. [+] [✓] Block 30 decrypted: 'chSwitchebennich'
  429. [*] [i] Block time: 11.08s
  430. [*] [+] Processing Block 29/51...
  431. [+] [✓] Block 29 decrypted: 'raufderWelthatsi'
  432. [*] [i] Block time: 10.70s
  433. [*] [+] Processing Block 28/51...
  434. [+] [✓] Block 28 decrypted: 'nderenGrosshndle'
  435. [*] [i] Block time: 11.10s
  436. [*] [+] Processing Block 27/51...
  437. [+] [✓] Block 27 decrypted: 'dersalsfastallea'
  438. [*] [i] Block time: 12.34s
  439. [*] [+] Processing Block 26/51...
  440. [+] [✓] Block 26 decrypted: 'eitJahrzehntenan'
  441. [*] [i] Block time: 9.56s
  442. [*] [+] Processing Block 25/51...
  443. [+] [✓] Block 25 decrypted: 'elauchberuflichs'
  444. [*] [i] Block time: 11.52s
  445. [*] [+] Processing Block 24/51...
  446. [+] [✓] Block 24 decrypted: 'zidbegleitetWeib'
  447. [*] [i] Block time: 8.56s
  448. [*] [+] Processing Block 23/51...
  449. [+] [✓] Block 23 decrypted: 'rgirlSchienensui'
  450. [*] [i] Block time: 12.67s
  451. [*] [+] Processing Block 22/51...
  452. [+] [✓] Block 22 decrypted: 'digkommtnochSupe'
  453. [*] [i] Block time: 11.40s
  454. [*] [+] Processing Block 21/51...
  455. [+] [✓] Block 21 decrypted: 'nGlasfasernotwen'
  456. [*] [i] Block time: 13.75s
  457. [*] [+] Processing Block 20/51...
  458. [+] [✓] Block 20 decrypted: 'ktuellerAusbauvo'
  459. [*] [i] Block time: 12.11s
  460. [*] [+] Processing Block 19/51...
  461. [+] [✓] Block 19 decrypted: 'denRestisteinpun'
  462. [*] [i] Block time: 14.10s
  463. [*] [+] Processing Block 18/51...
  464. [+] [✓] Block 18 decrypted: 'enStellenstattfr'
  465. [*] [i] Block time: 9.05s
  466. [*] [+] Processing Block 17/51...
  467. [+] [✓] Block 17 decrypted: 'ndenanvorgegeben'
  468. [*] [i] Block time: 12.14s
  469. [*] [+] Processing Block 16/51...
  470. [+] [✓] Block 16 decrypted: 'dereEventkmpfefi'
  471. [*] [i] Block time: 10.06s
  472. [*] [+] Processing Block 15/51...
  473. [+] [✓] Block 15 decrypted: 'KlubmitBossundan'
  474. [*] [i] Block time: 13.42s
  475. [*] [+] Processing Block 14/51...
  476. [+] [✓] Block 14 decrypted: 'eiltederBerliner'
  477. [*] [i] Block time: 10.88s
  478. [*] [+] Processing Block 13/51...
  479. [+] [✓] Block 13 decrypted: 'ttwochangesetztt'
  480. [*] [i] Block time: 11.50s
  481. [*] [+] Processing Block 12/51...
  482. [+] [✓] Block 12 decrypted: 'OperationistfrMi'
  483. [*] [i] Block time: 9.81s
  484. [*] [+] Processing Block 11/51...
  485. [+] [✓] Block 11 decrypted: 'ineKartezusetzen'
  486. [*] [i] Block time: 12.57s
  487. [*] [+] Processing Block 10/51...
  488. [+] [✓] Block 10 decrypted: 'hlauistallesaufe'
  489. [*] [i] Block time: 16.02s
  490. [*] [+] Processing Block 9/51...
  491. [+] [✓] Block 9 decrypted: 'nichtbesonderssc'
  492. [*] [i] Block time: 9.98s
  493. [*] [+] Processing Block 8/51...
  494. [+] [✓] Block 8 decrypted: 'lebewiesendasses'
  495. [*] [i] Block time: 11.68s
  496. [*] [+] Processing Block 7/51...
  497. [+] [✓] Block 7 decrypted: 'hichtezahlloseMa'
  498. [*] [i] Block time: 11.48s
  499. [*] [+] Processing Block 6/51...
  500. [+] [✓] Block 6 decrypted: 'rsseinhatdieGesc'
  501. [*] [i] Block time: 10.48s
  502. [*] [+] Processing Block 5/51...
  503. [+] [✓] Block 5 decrypted: 'nauchimmerwoande'
  504. [*] [i] Block time: 10.37s
  505. [*] [+] Processing Block 4/51...
  506. [+] [✓] Block 4 decrypted: 'undingutendaskan'
  507. [*] [i] Block time: 11.94s
  508. [*] [+] Processing Block 3/51...
  509. [+] [✓] Block 3 decrypted: 'nteninschlechten'
  510. [*] [i] Block time: 12.15s
  511. [*] [+] Processing Block 2/51...
  512. [+] [✓] Block 2 decrypted: 'chhintrumtinMome'
  513. [*] [i] Block time: 10.69s
  514. [*] [+] Processing Block 1/51...
  515. [+] [✓] Block 1 decrypted: 'womansichinnerli'
  516. [*] [i] Block time: 10.90s
  517. [*] All blocks successfully decrypted in this session.
  518. [*] Closed connection to goldenhaven-of-preposterous-cash.gpn23.ctf.kitctf.de port 443
  519. [*] All blocks decrypted. Proceeding to flag recovery.
  520. [+] Decrypted Challenge (unpadded): womansichinnerlichhintrumtinMomenteninschlechtenundingutendaskannauchimmerwoandersseinhatdieGeschichtezahlloseMalebewiesendassesnichtbesondersschlauistallesaufeineKartezusetzenOperationistfrMittwochangesetztteiltederBerlinerKlubmitBossundandereEventkmpfefindenanvorgegebenenStellenstattfrdenRestisteinpunktuellerAusbauvonGlasfasernotwendigkommtnochSupergirlSchienensuizidbegleitetWeibelauchberuflichseitJahrzehntenandersalsfastalleanderenGrosshndleraufderWelthatsichSwitchebennichtaufdasGeschftmitdenRegistrarenbeschrnktGesprchseiauchdassdieKapitalgeberihreBeteiligungeninGenossenschaftsanteileumwandelnunddasUnternehmenohneeinenVerkaufweiterfhrenHaighsjngstesWerkistebenvielmehralsdasEsisteineGeschichtedarberwieallgemeingltigromantischesChaosistdarberwieabsurdesistBeziehungenfrstarreDingezuhaltenEBOE
  521. [*]
  522. --- Final decrypted text (raw) ---
  523. womansichinnerlichhintrumtinMomenteninschlechtenundingutendaskannauchimmerwoandersseinhatdieGeschichtezahlloseMalebewiesendassesnichtbesondersschlauistallesaufeineKartezusetzenOperationistfrMittwochangesetztteiltederBerlinerKlubmitBossundandereEventkmpfefindenanvorgegebenenStellenstattfrdenRestisteinpunktuellerAusbauvonGlasfasernotwendigkommtnochSupergirlSchienensuizidbegleitetWeibelauchberuflichseitJahrzehntenandersalsfastalleanderenGrosshndleraufderWelthatsichSwitchebennichtaufdasGeschftmitdenRegistrarenbeschrnktGesprchseiauchdassdieKapitalgeberihreBeteiligungeninGenossenschaftsanteileumwandelnunddasUnternehmenohneeinenVerkaufweiterfhrenHaighsjngstesWerkistebenvielmehralsdasEsisteineGeschichtedarberwieallgemeingltigromantischesChaosistdarberwieabsurdesistBeziehungenfrstarreDingezuhaltenEBOE
  524. ----------------------------------
  525. [+] Potential Flag found (after removing 4 appended chars): GPNCTF{nic3_guEs5in6_p4DD1n6_iS_fUN}
  526. [*] Script finished.
  527. 14.424u 3.295s 9:42.46 3.0% 0+0k 0+0io 67pf+0w
  528.  
Tags: CTF GPNCTF
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement